home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1997 April / EnigmA AMIGA RUN 17 (1997)(G.R. Edizioni)(IT)[!][issue 1997-04][EAR-CD].iso / EARCD / comm / mmgr / MM_StarTrack.lha / MM / Rexx / MM_StarTrack.rexx < prev    next >
OS/2 REXX Batch file  |  1996-10-09  |  91KB  |  3,785 lines

  1. /*
  2.  
  3.                       $VER: MM_StarTrack 0.96  (09.10.96)
  4.  
  5.                            (C) 1994-96 Robert Hofmann
  6.  
  7. */
  8.  
  9. parse arg opts
  10.  
  11. options cache
  12. options failat 99
  13. options results
  14.  
  15. signal on break_c
  16. signal on break_d
  17. signal on break_e
  18. signal on break_f
  19. signal on halt
  20. signal on ioerr
  21. signal on syntax
  22.  
  23. address 'MAILMANAGER'
  24.  
  25.  
  26. Main:
  27.  
  28.     call Init
  29.     call Header
  30.     call Parse_Args(opts)
  31.     call Get_SystemDatas()
  32.     call Read_Cfg(0)
  33.     call pragma('p', system.taskpri)
  34.     call Wait_AreasWindow
  35.     call Check_NodeList
  36.  
  37.     do n=0 to system.mtrx.count-1
  38.         call Process_Msgs(system.mtrx.area.n, system.mtrx.addr.n)
  39.     end
  40.  
  41.     if system.cse.count>1    then call Bounce_SplitEncoded
  42.  
  43.     call Clean_Up
  44.  
  45. call Quit(0, 'All done.')
  46. exit
  47.  
  48.  
  49. Add_CheckSplitEncoded: procedure Expose msg. system.
  50.  
  51.     parse arg areatag, nr, size
  52.  
  53.     last    = system.cse.count-1
  54.     s            = 'a'x
  55.     check    = upper(msg.data.from || s || msg.data.fromaddr || s || msg.data.to || s || msg.data.toaddr)
  56.  
  57.     if last>-1    then
  58.         if system.cse.data.last~=check    then
  59.             do
  60.                 system.cse.area.count    = 0
  61.                 system.cse.msg.count    = 0
  62.                 system.cse.data.count    = 0
  63.             end
  64.  
  65.     MM_AddToStem 'system.cse.area'    'areatag'
  66.     MM_AddToStem 'system.cse.msg'        'nr'
  67.     MM_AddToStem 'system.cse.data'    'check'
  68.  
  69.     system.cse.count    = system.cse.area.count
  70.  
  71. return
  72.  
  73.  
  74. Add_Clean: procedure Expose system.
  75.  
  76.     arg area
  77.  
  78.     if find(system.clean, area)>0 then return
  79.  
  80.     system.clean    = system.clean area
  81.  
  82.     MM_AddToStem 'system.clean' 'area'
  83.  
  84. return
  85.  
  86.  
  87. Add_Clip: procedure
  88.  
  89.     arg area .
  90.  
  91.     tmp    = getclip('MM_EXPORT')
  92.  
  93.     if find(tmp, area)=0 then call setclip('MM_EXPORT', tmp area)
  94.  
  95. return
  96.  
  97.  
  98. Add_Found: procedure Expose found. system.
  99.  
  100.     parse arg line
  101.  
  102.     if index(found.check, line'a'x)>0 then return 0
  103.  
  104.     MM_AddToStem 'found' 'line'
  105.  
  106.     found.check    = found.check line'a'x
  107.  
  108. return 0
  109.  
  110.  
  111. Add_Kludge: procedure Expose msg. system. write.
  112.  
  113.     parse arg type, kludge
  114.  
  115.     stem.0    = 'msg.data.'
  116.     stem.1    = 'write.'
  117.     tmp            = '1'x || kludge
  118.  
  119.     MM_AddToStem stem.type'head' 'tmp'
  120.  
  121. return
  122.  
  123.  
  124. Add_Log: procedure Expose msg. system.
  125.  
  126.     parse arg pfx.1, text.1, pfx.2, text.2
  127.  
  128.     if text.1='' & text.2~='' then
  129.         do
  130.             pfx.1        = pfx.2
  131.             pfx.2     = ''
  132.             text.1    = text.2
  133.             text.2    = ''
  134.         end
  135.  
  136.     if text.1~='' then
  137.         do
  138.             do n=1 to 2
  139.                 if pfx.n~='' then    pfx.n    = overlay(pfx.n, system.log.prefix)
  140.                 else                            pfx.n    = system.log.etypfx
  141.             end
  142.  
  143.             text    = pfx.1 || text.1
  144.  
  145.             if text.2~='' then text = left(text, (system.log.linelen%2)-1) pfx.2 || text.2
  146.         end
  147.  
  148.     MM_AddToStem 'msg.log' 'text'
  149. return
  150.  
  151.  
  152. Add_Stat: procedure Expose statistic.
  153.  
  154.     parse arg text, num .
  155.  
  156.     if num~='' then text = overlay('   'text' ', '   ......................:') right(num, 5)
  157.  
  158.     MM_AddToStem 'statistic' 'text'
  159. return
  160.  
  161.  
  162. Add_Status: procedure Expose msg.
  163.  
  164.     parse arg line
  165.  
  166.     MM_AddToStem 'msg.status' 'line'
  167. return
  168.  
  169.  
  170. Add_Via: procedure Expose msg. system.
  171.  
  172.     arg stem
  173.     tmp    = '1'x'Via' system.addr delstr(date(), 8, 2)'  'time() '('system.prg.fid')'
  174.     MM_AddToStem stem 'tmp'
  175. return
  176.  
  177.  
  178. Add_Write: procedure Expose write.
  179.  
  180.     parse arg text
  181.     MM_AddToStem 'write.text' 'text'
  182. return
  183.  
  184.  
  185. Adjust_Addresses: procedure Expose domain. msg. system.
  186.  
  187.     from_addr    = Check_Addr(msg.data.fromaddr)
  188.     to_addr        = Check_Addr(msg.data.toaddr  )
  189.     adj                = 0
  190.  
  191.     if from_addr~=''    then
  192.         if upper(msg.data.fromaddr)~=upper(from_addr) then
  193.             do
  194.                 msg.data.fromaddr    = from_addr
  195.                 call Set_MsgChanged
  196.             end
  197.  
  198.     if to_addr~=''        then
  199.         if upper(msg.data.toaddr)~=upper(to_addr)            then
  200.             do
  201.                 msg.data.toaddr        = to_addr
  202.                 call Set_MsgChanged
  203.             end
  204.  
  205. return
  206.  
  207.  
  208. Adjust_Kludges: procedure Expose msg. system.
  209.  
  210.     new.    = 0
  211.  
  212.     do n=0 to msg.data.head.count-1
  213.         parse value upper(msg.data.head.n) with . 2 kludge .
  214.  
  215.         if find(system.rmkludges, kludge)=0 then MM_AddToStem 'new' 'msg.data.head.'n
  216.     end
  217.  
  218.     if msg.data.head.count~=new.count then
  219.         do
  220.             call Drop_SubStem('msg.data.head')
  221.  
  222.             do n=0 to new.count-1
  223.                 msg.data.head.n    = new.n
  224.             end
  225.  
  226.             msg.data.head.count    = new.count
  227.         end
  228.  
  229. return
  230.  
  231.  
  232. Analyse_Kludges: procedure Expose msg. domain. system.
  233.  
  234.     msg.data.kludge.reply    = Get_Kludge('MSGID:',    '1'x'REPLY:')
  235.     msg.data.replyaddr        = Get_Kludge('REPLYADDR', 'To:')
  236.     tmp                                        = Get_Kludge('REPLYTO')
  237.  
  238.     if tmp~='' then
  239.         do
  240.             parse var tmp check name
  241.             address = Check_Addr(check)
  242.  
  243.             if address='' then break
  244.  
  245.             msg.data.fromaddr = address
  246.  
  247.             name = strip(name)
  248.             if name~='' then msg.data.from = name
  249.         end
  250.  
  251.     msg.data.kludges = 1
  252.  
  253. return
  254.  
  255.  
  256. BaseName: procedure
  257.  
  258.     parse arg file
  259.  
  260. return substr(file, max(lastpos(':', file), lastpos('/', file))+1)
  261.  
  262.  
  263. Both_Unknown: procedure Expose msg. system.
  264.  
  265.     parse arg area, nr
  266.  
  267.     call Log('   UNKKNOWN SOURCE & DESTINATION!!!')
  268.     call Move_Msg(area, nr, system.badarea, 'Unknown source & destination')
  269.     call Add_Status('SOURCE and DESTINATION-address unknown or incorrect, msg stopped!')
  270.     call Count_Stat('UNKNDST')
  271.     call Count_Stat('UNKNSRC')
  272. return
  273.  
  274.  
  275. Bounce_Mail: procedure Expose domain. msg. system.
  276.  
  277.     parse arg area, nr
  278.  
  279.     call Log('   UNKKNOWN DESTINATION!!!  From' msg.data.from 'to' msg.data.to)
  280.  
  281.     call Forward_Msg('Bounced', msg.data.fromlang, area, msg.data.from, msg.data.fromaddr,,
  282.                                         Get_Text(msg.data.fromlang, 'SUBJ', 'BOUNCE_UNKNDST'),, 1)
  283.  
  284.     call Move_Msg(area, nr, system.badarea, 'Unknown destination')
  285.  
  286.     call Add_Status('Unknown DESTINATION-address detected:' msg.data.toaddr', *** BOUNCED ***')
  287.     call Count_Stat('UNKNDST')
  288. return
  289.  
  290.  
  291. Bounce_SplitEncoded: procedure Expose domain. system.
  292.  
  293.     do n=0 to system.cse.count-1
  294.         dmn        = Read_Msg(system.cse.area.n, system.cse.msg.n)
  295.         last    = n-1
  296.  
  297.         call Add_Clean(system.cse.area.n)
  298.  
  299.         if system.cse.data.last~=system.cse.data.n    then
  300.             do
  301.                 call Log(' Splitted encoded mails detected:' system.cse.area.n', starting at msg #'system.cse.msg.n)
  302.                 call Bounce_Twit('Split-Encoded', system.cse.area.n, system.cse.msg.n, domain.dmn.encoded.mode)
  303.  
  304.                 move    = find(domain.dmn.encoded.mode, 'MOVE')>0
  305.  
  306.                 call Count_Stat('ENCODED')
  307.             end
  308.         else
  309.             if move then call Move_Msg(system.cse.area.n, system.cse.msg.n, system.badarea, 'Splitted Encoded Mail')
  310.             else
  311.                 do
  312.                     MM_DeleteMsg system.cse.area.n system.cse.msg.n
  313.                     call Log('   Msg #' system.cse.msg.n 'deleted!')
  314.                 end
  315.  
  316.         call Log_Msg(dmn)
  317.     end
  318.  
  319. return
  320.  
  321.  
  322. Bounce_Twit: procedure Expose domain. msg. system.
  323.  
  324.     parse arg kind, area, nr, mode
  325.  
  326.     ukind = upper(kind)
  327.  
  328.     call Log('   'ukind 'MAIL!!!  From' msg.data.from 'to' msg.data.to)
  329.  
  330.     if find(mode, 'BOUNCE')>0 then
  331.         do
  332.             call Forward_Msg(kind, msg.data.fromlang, area, msg.data.from, msg.data.fromaddr,,
  333.                                              Get_Text(msg.data.fromlang, 'SUBJ', 'BOUNCE_'compress(ukind, '-')),, 1)
  334.  
  335.             info    = '*** NETMAIL BOUNCED ***'
  336.             ret        = 1
  337.         end
  338.     else
  339.         do
  340.             info    = ''
  341.             ret        = 0
  342.         end
  343.  
  344.     if find(mode, 'MOVE')>0 then call Move_Msg(area, nr, system.badarea, kind)
  345.     else
  346.         do
  347.             MM_DeleteMsg area nr
  348.             call Log('   Msg deleted!')
  349.         end
  350.  
  351.     call Add_Status(ukind 'MAIL detected!!!' info)
  352.     call Count_Stat(ukind)
  353. return ret
  354.  
  355.  
  356. break_c:; break_d:; break_e:; break_f:; halt:
  357.  
  358.     signal off break_c
  359.     signal off break_d
  360.     signal off break_e
  361.     signal off break_f
  362.     signal off halt
  363.  
  364.     return_code        =    5
  365.     error_line    = 0
  366.     error_msg            = 'Execution halted!!!'
  367.     rc                        = 0
  368. signal Exit
  369.  
  370.  
  371. Check_Addr: procedure Expose domain. system.
  372.  
  373.     parse arg check, mode
  374.  
  375.     parse value strip(check, 'b', '. ') with zone ':' net '/' node '@' dmn '.' .
  376.     parse var node node '.' point .
  377.  
  378.     if point='' then point = 0
  379.  
  380.     if ~datatype(zone, 'N') | ~datatype(net, 'N') | ~datatype(node, 'N') | ~datatype(point, 'N'),
  381.      | zone=0 | net=0 then return ''
  382.  
  383.     if mode~='NOADJ' then
  384.         do
  385.             tmp = system.domain
  386.             if domain='' | domain.tmp.adjust | mode='ADJUST' then dmn = Get_Domain(check)
  387.         end
  388.     else
  389.         if dmn='' | find(system.vdomains, Make_Valid(dmn))=0  then return ''
  390.  
  391. return zone':'net'/'node'.'point'@'dmn
  392.  
  393.  
  394. Check_Address: procedure Expose domain. msg. system.
  395.  
  396.     arg mode, addr . 1 zone . ':' .
  397.  
  398.     node    = mode'NODE'
  399.     real    = mode'ADDR'
  400.     tmp        = mode'DOMAIN'
  401.     dmn        = msg.data.tmp
  402.  
  403.     if find(system.addresses, msg.data.node)>0 then
  404.         return find(system.addresses system.nodes, upper(msg.data.real))=0
  405.  
  406.     if find(system.nodes, msg.data.node)>0 then return 0
  407.  
  408.     if domain.dmn.zones~='' & domain.dmn.bounce.wrongaddr then
  409.         if find(domain.dmn.zones, zone)=0    | dmn=system.baddomain then return 1
  410.  
  411.     if ~domain.dmn.bounce.unkndst then return 0
  412.  
  413.     MM_GetNodelistNode msg.data.node 'tmp'; ret = rc
  414.  
  415.     if ret=0 then return 0
  416.     if ret=5 then
  417.         do
  418.             call Log('*** INFO: Unable to access nodelist(s)!!!')
  419.             return 0
  420.         end
  421.  
  422. return 1
  423.  
  424.  
  425. Check_AllowEncoded: procedure Expose domain. msg. system.
  426.  
  427.     parse arg dmn
  428.  
  429.     if ~domain.dmn.fkt.allowenc    then return 0
  430.  
  431.     enc.0 = 'FROM'
  432.     enc.1    = 'TO'
  433.  
  434.     do n=0 to 1
  435.         typ        = enc.n
  436.         field    = typ'ADDR'
  437.  
  438.         do m=0 to domain.dmn.encoded.typ.count-1
  439.             if Check_Pattern(domain.dmn.encoded.typ.m, msg.data.field) then return 1
  440.         end
  441.     end
  442.  
  443. return 0
  444.  
  445.  
  446. Check_CrossNet: procedure Expose msg. domain.
  447.  
  448.     parse arg dmn
  449.  
  450.     if msg.data.fromdomain~=msg.data.todomain then return 1+domain.dmn.bounce.crossnet
  451.  
  452. return 0
  453.  
  454.  
  455. Check_Empty: procedure Expose domain. msg. system.
  456.  
  457.     arg dmn, address
  458.  
  459.     if ~domain.dmn.delete.empty | ~msg.system    then return 0
  460.  
  461.     check = 0
  462.     tear    = 0
  463.  
  464.     do n=0 to msg.data.text.count-1 while ~(check | tear)
  465.         parse var msg.data.text.n first .
  466.  
  467.         tear    = first='---'
  468.         check = strip(msg.data.text.n)~='' & ~tear
  469.     end
  470.  
  471. return check=0
  472.  
  473.  
  474. Check_Encoded: procedure Expose domain. msg. system.
  475.  
  476.     arg area, nr, dmn, msgsize
  477.  
  478.     if msg.system | msg.link            then return ''
  479.     if domain.dmn.encoded.size=0    then return ''
  480.  
  481.     check_split    = find(domain.dmn.encoded.mode, 'SPLIT')>0
  482.  
  483.     if msgsize<domain.dmn.encoded.size & ~check_split    then return ''
  484.  
  485.     enc            = 0
  486.     enc_len    = 0
  487.  
  488.     do n=msg.data.text.count-1 to 0 by -1
  489.         len        = length(msg.data.text.n)
  490.         check    = len-(len-length(compress(msg.data.text.n)))-lastpos(' ', msg.data.text.n)
  491.  
  492.         if last_len~=check then cnt = 0
  493.  
  494.         last_len = len
  495.  
  496.         if len<50    then iterate
  497.         if cnt>0    then enc_len    = enc_len + last_len
  498.  
  499.         cnt    = cnt+1
  500.  
  501.         if cnt<max(domain.dmn.encoded.size%len, 10) then iterate
  502.  
  503.         enc = 1
  504.         leave
  505.     end
  506.  
  507.     if enc | (~enc & check_split)            then    allowed    = ~enc | Check_AllowEncoded(dmn)
  508.     else                                                                        allowed    = 1
  509.  
  510.     if ~enc & ~allowed & check_split    then    call Add_CheckSplitEncoded(area, nr, enc_len)
  511.  
  512.     if allowed                                                then    ret         = ''
  513.     else                                                                        ret            = '*' domain.dmn.encoded.mode
  514.  
  515. return ret
  516.  
  517.  
  518. Check_FATT: procedure Expose msg. domain. rc. system.
  519.  
  520.     arg dmn
  521.  
  522.     if ~domain.dmn.fatt                                then return 0
  523.     if find(msg.data.flags, 'FATT')=0 then return 0
  524.     if msg.sysop                                            then return 1
  525.     if msg.system | msg.link                    then return 2
  526.  
  527. return 3
  528.  
  529.  
  530. Check_Function: procedure Expose domain. msg. rc. system.
  531.  
  532.     arg function
  533.  
  534.     ret    = Check_Matching_Pattern(function,    msg.data.from,    msg.data.fromaddr,    msg.data.subj),
  535.                 Check_Matching_Pattern(function,    msg.data.to,        msg.data.toaddr,        msg.data.subj)
  536.  
  537. return Set_RC(function, strip(ret))
  538.  
  539.  
  540. Check_Hex: procedure
  541.  
  542.     arg hex hash ., len
  543.  
  544. return hex~='' & datatype(hex, 'X') & length(hex)=len & d2x(hash(hex))=hash
  545.  
  546.  
  547. Check_Kill: procedure Expose msg. system.
  548.  
  549.     if ~system.fkt.kill | ~msg.sysop then return ''
  550.  
  551. return Check_Matching_Pattern('KILL', msg.data.from, msg.data.fromaddr, msg.data.subj)
  552.  
  553.  
  554. Check_Loop: procedure Expose domain. msg. system.
  555.  
  556.     arg check, area, nr
  557.  
  558.     if ~check then return 0
  559.  
  560.     check.        = 0
  561.     last_addr    = ''
  562.  
  563.     do n=0 to msg.data.foot.count-1
  564.         if compress(msg.data.foot.n, '010D'x)='' then iterate
  565.  
  566.         addr = upper(Get_Addr_From_Via(msg.data.foot.n))
  567.  
  568.         select
  569.             when addr='???'    then nop
  570.             when check.addr    & last_addr~=addr & find(system.addresses, addr)>0 then return 1
  571.             otherwise
  572.                 do
  573.                     check.addr    = 1
  574.                     last_addr        = addr
  575.                 end
  576.         end
  577.     end
  578.  
  579. return 0
  580.  
  581.  
  582. Check_Matching_Pattern: procedure Expose msg. system.
  583.  
  584.     arg type, stem.name.0, stem.addr.0, stem.subj.0
  585.  
  586.     if system.fkt.np.type    then
  587.         do n=0 to system.check.count-1
  588.             field                            = system.check.n
  589.             stem.field.count    = 1
  590.  
  591.             do m=0 to system.type.field.ptrn.count-1
  592.                 result.    = 0
  593.                 MM_SearchInStem 'stem.'field 'result' system.type.field.ptrn.m 'STR'
  594.  
  595.                 if result.count>0 then return system.type.field.mode.m
  596.             end
  597.         end
  598.  
  599.     if system.fkt.fp.type    then
  600.         do n=0 to system.ptrn.type.from.count-1
  601.             cnt                    = 0
  602.             stem.count    = 1
  603.  
  604.             do m=0 to system.full_check.count-1
  605.                 result.            = 0
  606.                 field                = system.full_check.m
  607.                 stem.0            = msg.data.field
  608.  
  609.                 MM_SearchInStem 'stem' 'result' '"'system.ptrn.type.field.n'"' 'NUM'
  610.  
  611.                 cnt    = cnt+(result.count>0)
  612.             end
  613.  
  614.             if cnt=system.full_check.count then return system.ptrn.type.mode.n
  615.         end
  616.  
  617. return ''
  618.  
  619.  
  620. Check_NodeList: procedure Expose system.
  621.  
  622.     check    = Get_Node(system.addresses.0)
  623.     tmp        = 'Unable to access nodelist(s)!!!'
  624.  
  625.     do n=0 to 5
  626.         MM_GetNodelistNode check 'tmp'
  627.         err = RC>0
  628.  
  629.         if ~err then leave
  630.  
  631.         call Log('*** WARNING:' tmp '('check 'not found)')
  632.         call Log('             'n'> Waiting 30 seconds...')
  633.         call delay(30*50)
  634.     end
  635.  
  636.     if err then
  637.         do
  638.             if system.cplnl~=0 then
  639.                 do
  640.                     call Log(' ==>> Compiling nodelists!')
  641.                     call Command(system.cplnl)
  642.  
  643.                     MM_GetNodelistNode check 'tmp'
  644.                     err = RC>0
  645.                 end
  646.  
  647.             if err then call Quit(24, tmp)
  648.         end
  649.  
  650. return
  651.  
  652.  
  653. Check_Pattern: procedure Expose result
  654.  
  655.     arg pattern, string.0
  656.  
  657.     string.count    = 1
  658.     result.                = 0
  659.  
  660.     MM_SearchInStem 'string' 'result' '"'pattern'"' 'STR'
  661.  
  662.     result                = result.0
  663.  
  664. return result.count>0
  665.  
  666.  
  667. Check_Processed: procedure Expose domain. system.
  668.  
  669.     /* If you disable this routine, you are not allowed to use MM_StarTrack anymore! */
  670.  
  671.     system.stat.ucnt    = system.stat.ucnt+1
  672.     last_check                = system.stat.uchk
  673.     system.stat.uchk    = 12+left(date('u'), 2)
  674.     tmp               = date('i')-system.stat.uday
  675.  
  676.     if system.stat.uchk=last_check | tmp<30 | datatype(substr(system.prg.state, 2), 'N') then return
  677.  
  678.     call Msg_Head('4E6F74696669636174696F6E2061626F757420746865207573616765206F66'x system.prg.fid)
  679.     call Get_SystemInfo()
  680.     call Notify_Author('5573616765206F66'x system.prg.name)
  681.  
  682. return
  683.  
  684.  
  685. Check_Robot: procedure Expose msg. system.
  686.  
  687.     if ~msg.sysop then return 0
  688.  
  689.     arg to
  690.  
  691.     do n=0 to system.robot.name.ptrn.count-1
  692.         if Check_Pattern(system.robot.name.ptrn.n, to) then return 1
  693.     end
  694.  
  695. return 0
  696.  
  697.  
  698. Check_RRR: procedure Expose domain. msg. system.
  699.  
  700.     arg dmn
  701.  
  702.     if find(msg.data.flags, 'RRR')=0 then return 0
  703.  
  704.     if domain.dmn.rrr.system then
  705.         if msg.sysop then return 1
  706.  
  707.     if domain.dmn.rrr.points then
  708.         if find(system.addresses, upper(msg.data.tonode))>0    then return 1
  709.  
  710. return 0
  711.  
  712.  
  713. Check_Twit: procedure Expose domain. msg. rc. system.
  714.  
  715.     if msg.sysop then return ''
  716.  
  717. return Check_Function('TWIT')
  718.  
  719.  
  720. Clean_Up:
  721.  
  722.     if ~system.nostats & system.stats then call Write_Stats
  723.  
  724.     if system.export.count=0 & system.clean.count>0 then
  725.         do n=0 to system.clean.count-1
  726.             MM_CleanArea system.clean.n
  727.         end
  728.  
  729.     do n=0 to system.export.count-1
  730.         MM_Export            system.export.n
  731.         MM_Delete            system.export.n
  732.         MM_CleanArea  system.export.n
  733.     end
  734.  
  735.     call delete(system.tmpfile)
  736. return
  737.  
  738.  
  739. Command: procedure Expose system.
  740.  
  741.     parse arg cmd
  742.  
  743.     address command cmd
  744.  
  745.     if rc>0 then call Log('*** ERROR: Command "'cmd'" returned' rc'.')
  746. return rc
  747.  
  748.  
  749. Count_Stat: procedure Expose system.
  750.  
  751.     arg mode ., cnt
  752.  
  753.     if cnt=''                                    then cnt                            = 1
  754.     if system.stat.mode>65534 then system.stat.mode = 0
  755.  
  756.     system.stat.mode = system.stat.mode+cnt
  757. return
  758.  
  759.  
  760. Cross_Net: procedure Expose domain. msg. system.
  761.  
  762.     parse arg area, nr, mode, src
  763.  
  764.     call Log('   CROSSNET!!!')
  765.  
  766.     if mode<2 then
  767.         do
  768.             call Forward_Msg('CrossNet_ToDst', msg.data.tolang, area, msg.data.to, msg.data.toaddr,,
  769.                                                 Get_Text(msg.data.tolang, 'SUBJ', 'CROSSNET_TODST'),, 0)
  770.  
  771.             info    = 'Info added.'
  772.         end
  773.     else info = '*** BOUNCED ***'
  774.  
  775.     if ~src then
  776.         do
  777.             if mode<2 then tmp = 'CROSSNET_TOSRC'
  778.             else                     tmp = 'BOUNCE_CROSSNET'
  779.  
  780.             call Forward_Msg('CrossNet_ToSrc', msg.data.fromlang, area, msg.data.from, msg.data.fromaddr,,
  781.                                                 Get_Text(msg.data.fromlang, 'SUBJ', tmp),, 1)
  782.  
  783.             resend = 'Msg resend.'
  784.         end
  785.     else resend = ''
  786.  
  787.     call Move_Msg(area, nr, system.badarea, 'Cross-Net')
  788.  
  789.     call Add_Status(strip('Crossnet-Netmail detected!' info resend))
  790.     call Count_Stat('CROSSNET')
  791. return
  792.  
  793.  
  794. Cut_Text: procedure Expose msg.
  795.  
  796.     parse arg num
  797.  
  798.     tmp                                    = num+1
  799.     msg.data.text.num        = '[...]'
  800.     msg.data.text.tmp        = ''
  801.     msg.data.text.count    = num+2
  802.  
  803. return
  804.  
  805.  
  806. d2h: procedure
  807.  
  808.     arg num
  809. return right(d2x(num), 4, '0')
  810.  
  811.  
  812. Debug: procedure Expose system.
  813.  
  814.     if ~system.debug then return
  815.  
  816.     parse arg pfx, line
  817.  
  818.     if line~='' then line = 'DEBUG -' pfx':' line
  819.  
  820.     call Log(line, '*')
  821.  
  822. return
  823.  
  824.  
  825. Del_Flag: procedure Expose msg.
  826.  
  827.     arg flag
  828.  
  829.     p    = find(msg.data.flags, flag)
  830.  
  831.     if p>0    then  msg.data.flags = delword(msg.data.flags, p, 1) '!'flag
  832.  
  833. return
  834.  
  835.  
  836. Drop_SubStem:
  837.  
  838.     arg stem
  839.  
  840.     interpret "do nn=0 to" stem".count-1; drop" stem".nn; end;" stem".count = 0"
  841. return
  842.  
  843.  
  844. Execute_Cmd: procedure Expose msg. rc. system.
  845.  
  846.     parse arg area, nr
  847.  
  848.     call Get_FunctionDatas('EXECUTE')
  849.  
  850.     do n=0 to found.count-1
  851.         cmd    = translate(found.n, '2227'x, 'FEFF'x)
  852.         cmd    = Replace_Embedded(cmd)
  853.         cmd    = Replace(cmd, area, '%a')
  854.         cmd    = Replace(cmd, nr,   '%n')
  855.         p        = pos('%T', cmd)
  856.  
  857.         if p>0 then
  858.             do
  859.                 tmp_l =     left(cmd, p-1)
  860.                 tmp_r    = substr(cmd, p+2)
  861.  
  862.                 parse var tmp_r msgfile tmp_r
  863.  
  864.                 cmd    = strip(strip(tmp_l) strip(tmp_r))
  865.  
  866.                 MM_WriteStem msgfile 'msg.data.head'
  867.                 MM_WriteStem msgfile 'msg.data.text' 'APPEND'
  868.                 MM_WriteStem msgfile 'msg.data.foot' 'APPEND'
  869.             end
  870.  
  871.         call Log('   Executing "'cmd'".',, 3)
  872.  
  873.         ret = Command(cmd)
  874.  
  875.         select
  876.             when ret=0    then    nop
  877.             when ret=1    then    call Set_RC('KILL',            rc.kill 'BOUNCE MOVE')
  878.             when ret=2    then    call Set_RC('TWIT',            rc.twit 'BOUNCE MOVE')
  879.             otherwise                    call Set_RC('EXCLUDE',    '*')
  880.         end
  881.     end
  882.  
  883. return
  884.  
  885.  
  886. Exit:
  887.  
  888.     if return_code>29 then call Report_Error(return_code, error, error_msg)
  889.  
  890.     select
  891.         when return_code>=40 then error = 'INTERNAL-ERROR:'
  892.         when return_code>=30 then error = 'IO-ERROR:'
  893.         when return_code>=20 then error = 'ERROR:'
  894.         when return_code>=10 then error = 'CFG-ERROR:'
  895.         when return_code>=5  then error = 'INFO:'
  896.         otherwise                                    error = ''
  897.     end
  898.  
  899.     txt    = '***' strip(error error_msg) '***'
  900.  
  901.     if return_code>5 then    say system.prg.name':' txt
  902.  
  903.     call Log(,, 3)
  904.     call Log(txt, '+')
  905.     call Log(, '\', 3)
  906.  
  907.     call setclip('MM_LogPre', system.mm.logpre)
  908.  
  909. exit return_code
  910.  
  911.  
  912. Format_Log: procedure Expose msg. system.
  913.  
  914.     parse arg pfx, text
  915.  
  916.     text    = strip(text)
  917.  
  918.     if text='' then
  919.         do
  920.             call Add_Log(pfx)
  921.             return
  922.         end
  923.  
  924.     if length(word(text, 1))>system.log.txtlen then
  925.         do
  926.             tmp        = substr(text, system.log.txtlen+1)
  927.             text    = strip(left(text, system.log.txtlen))
  928.             call         Add_Log(pfx, text)
  929.             call         Format_Log('', tmp)
  930.             return
  931.         end
  932.  
  933.     tlen = length(text)
  934.  
  935.     if tlen>system.log.txtlen then
  936.         do
  937.             lsp        = lastpos(' ', text, system.log.txtlen)
  938.             tmp        = substr(text, lsp)
  939.             text    = strip(left(text, lsp-1))
  940.             call        Add_Log(pfx,  text)
  941.             call        Format_Log('', tmp)
  942.         end
  943.     else call        Add_Log(pfx,  text)
  944.  
  945. return
  946.  
  947.  
  948. Forward: procedure Expose domain. msg. rc. system.
  949.  
  950.     parse arg mtrx, nr
  951.  
  952.     call Get_FunctionDatas('FORWARD')
  953.  
  954.     do n=0 to found.count-1
  955.         parse value translate(found.n, '2227'x, 'FEFF'x) with to.area '¡' to.name '¡' to.addr '¡' to.subj '¡' to.flags '¡' delmsg
  956.  
  957.         if to.area='%ma'    then to.area    = mtrx
  958.  
  959.         call Log('   Forwarding mail from' msg.data.from 'to' to.area',' to.name || strip(',' to.addr, 't', ', ')'...')
  960.  
  961.         MM_GetAreaInfo to.area 'tmp'
  962.         if tmp.type='MAIL' then    lang    = msg.data.tolang
  963.         else                                        lang    = Get_Language(tmp.addr)
  964.  
  965.         call Forward_Msg('Forward', lang, to.area, strip(to.name), to.addr,,
  966.                                             Replace_Embedded(strip(to.subj)), to.flags, 0)
  967.  
  968.         call Add_Clip(to.area)
  969.  
  970.         if delmsg    then
  971.             do
  972.                 MM_DeleteMsg mtrx nr
  973.                 call Log('   -> Original msg deleted!')
  974.             end
  975.     end
  976.  
  977. return delmsg
  978.  
  979.  
  980. Forward_Msg: procedure Expose domain. msg. system. write.
  981.  
  982.     parse arg file, language, area, dst, dstaddr, subject, flags, is_reply
  983.  
  984.     if file~='Forward' then
  985.         do
  986.             tmp = Get_Node(dstaddr)
  987.  
  988.             MM_GetNodelistNode tmp 'tmp'
  989.             if RC~=0 then
  990.                 do
  991.                     call Log('   *** Unknown destination-address' dstaddr', unable to forward mail!')
  992.                     return
  993.                 end
  994.  
  995.             dst = Get_Sysop(dst, dstaddr)
  996.  
  997.             call Log('   Sending mail to' dst',' dstaddr,, 3)
  998.         end
  999.  
  1000.     subject        = Replace_Embedded(subject)
  1001.     textfile    = Read_File(file, language)
  1002.  
  1003.     write. = 0
  1004.  
  1005.     if is_reply then
  1006.         do
  1007.             if ~msg.data.kludges then call Analyse_Kludges
  1008.             if msg.data.replyaddr~='' then MM_AddToStem 'write.addtxt' 'msg.data.replyaddr'
  1009.  
  1010.             MM_AddToStem 'write.head' 'msg.data.kludge.reply'
  1011.         end
  1012.  
  1013.     do n=0 to txt.count-1
  1014.         tmp    = Replace_Embedded(txt.n)
  1015.  
  1016.         if index(tmp, '%T')=0 then call Add_Write(tmp)
  1017.         else
  1018.             do
  1019.                 parse var tmp . '%T' lines .
  1020.  
  1021.                 if lines='' | lines=msg.data.text.count then lines = msg.data.text.count
  1022.                 else
  1023.                     do
  1024.                         if ~datatype(lines, 'N') then
  1025.                             do
  1026.                                 lines = 20
  1027.                                 call Log('*** WARNING: Numeric value expected after %T in' textfile 'at line' n'!!!')
  1028.                                 call Log('             -> Using default (='lines') instead.',, 4)
  1029.                             end
  1030.  
  1031.                         call Cut_Text(lines)
  1032.                     end
  1033.  
  1034.                 do m=0 to msg.data.head.count-1
  1035.                     tmp = strip(translate(msg.data.head.m, '@', '010D'x))
  1036.                     if tmp~='' then call Add_Write(tmp)
  1037.                 end
  1038.  
  1039.                 if msg.data.head.count>0 then call Add_Write()
  1040.  
  1041.                 do m=0 to msg.data.text.count-1
  1042.                     call Add_Write(msg.data.text.m)
  1043.                 end
  1044.  
  1045.                 do m=0 to msg.data.foot.count-1
  1046.                     tmp = strip(translate(msg.data.foot.m, '@', '010D'x))
  1047.                     if tmp~='' then call Add_Write(tmp)
  1048.                 end
  1049.             end
  1050.     end
  1051.  
  1052.     call Write_Msg('write', area, dst, dstaddr, subject, system.tmpfile, flags)
  1053. return
  1054.  
  1055.  
  1056. Get_Addr_From_Via: procedure Expose domain. system.
  1057.  
  1058.     parse arg . tmp
  1059.  
  1060.     tmp = compress(tmp, '010D'x',"')
  1061.  
  1062.     do while tmp>''
  1063.         parse var tmp check tmp
  1064.         address = Check_Addr(check)
  1065.  
  1066.         if address~='' then return address
  1067.     end
  1068.  
  1069. return '???'
  1070.  
  1071.  
  1072. Get_AddrDomain: procedure Expose system.
  1073.  
  1074.     arg .'@' domain '.' .
  1075.  
  1076. return Make_Valid(domain)
  1077.  
  1078.  
  1079. Get_Arg: procedure Expose args
  1080.  
  1081.     arg keyword, rnr
  1082.  
  1083.     p     = find(upper(args), keyword)
  1084.     ret    = 0
  1085.  
  1086.     if rnr>0 then
  1087.         if p>0 then
  1088.             do
  1089.                 ret        = subword(args, p+1, rnr)
  1090.                 args    = delword(args, p, p+rnr)
  1091.             end
  1092.         else ret            = ''
  1093.     else
  1094.         if p>0 then
  1095.             do
  1096.                 ret        = 1
  1097.                 args    = delword(args, p, 1)
  1098.             end
  1099.  
  1100.     args    = strip(args)
  1101.  
  1102. return ret
  1103.  
  1104.  
  1105. Get_Domain: procedure Expose domain. system.
  1106.  
  1107.     arg zone . ':' .
  1108.  
  1109.     l_tmp = system.domains
  1110.     u_tmp    = system.vdomains
  1111.  
  1112.     do while u_tmp~=''
  1113.         parse var l_tmp l_dmn l_tmp
  1114.         parse var u_tmp u_dmn u_tmp
  1115.  
  1116.         if find(domain.u_dmn.zones, zone)>0 then return l_dmn
  1117.     end
  1118.  
  1119.     parse var system.badaddr . '@' dmn '.' .
  1120.  
  1121. return dmn
  1122.  
  1123.  
  1124. Get_FunctionDatas: procedure Expose found. msg. system.
  1125.  
  1126.   parse arg function
  1127.  
  1128.     field.1            = 'FROM'
  1129.     field.2            = 'TO'
  1130.     found.            = 0
  1131.     found.check    = ''
  1132.     stem.subj.0    = msg.data.subj
  1133.  
  1134.     do i=1 to 2
  1135.         tmp                    = field.i
  1136.         tmp2                = tmp'ADDR'
  1137.         stem.name.0    =    msg.data.tmp
  1138.         stem.addr.0    = msg.data.tmp2
  1139.  
  1140.         do n=0 to system.check.count-1
  1141.             field                            = system.check.n
  1142.             stem.field.count    = 1
  1143.  
  1144.             do m=0 to system.function.field.ptrn.count-1
  1145.                 result.    = 0
  1146.                 MM_SearchInStem 'stem.'field 'result' system.function.field.ptrn.m 'STR'
  1147.  
  1148.                 if result.count>0 then call Add_Found(system.function.field.mode.m)
  1149.             end
  1150.         end
  1151.  
  1152.         do n=0 to system.ptrn.function.from.count-1
  1153.             cnt                    = 0
  1154.             stem.count    = 1
  1155.  
  1156.             do m=0 to system.full_check.count-1
  1157.                 result.            = 0
  1158.                 field                = system.full_check.m
  1159.                 stem.0            = msg.data.field
  1160.  
  1161.                 MM_SearchInStem 'stem' 'result' '"'system.ptrn.function.field.n'"' 'NUM'
  1162.  
  1163.                 cnt    = cnt+(result.count>0)
  1164.             end
  1165.  
  1166.             if cnt=system.full_check.count then cnt    = Add_Found(system.ptrn.function.mode.n)
  1167.         end
  1168.     end
  1169.  
  1170. return
  1171.  
  1172.  
  1173. Get_Kludge: procedure Expose msg. system.
  1174.  
  1175.     arg kludge, new_id
  1176.  
  1177.     result.     = 0
  1178.     result.0    = ''
  1179.  
  1180.     MM_SearchInStem 'msg.data.head' 'result' '"?'kludge' #?"' 'STR'
  1181.  
  1182.     parse var result.0 . ret
  1183.  
  1184.     ret = strip(ret)
  1185.  
  1186.     if ret~='' then ret = strip(new_id ret)
  1187.  
  1188. return ret
  1189.  
  1190.  
  1191. Get_Language: procedure Expose system.
  1192.  
  1193.     parse arg tmp_stem.0
  1194.     tmp_stem.count    = 1
  1195.     result.                    = 0
  1196.  
  1197.     do n=0 to system.lang.known.count-1
  1198.         tmp    = system.lang.known.n
  1199.  
  1200.         do m=0 to system.lang.ptrn.tmp.count-1
  1201.             MM_SearchInStem 'tmp_stem' 'result' system.lang.ptrn.tmp.m 'STR'
  1202.             if result.count>0 then return tmp
  1203.         end
  1204.     end
  1205.  
  1206. return ''
  1207.  
  1208.  
  1209. Get_Name: procedure
  1210.  
  1211.     parse arg address
  1212.  
  1213.     MM_GetNodelistNode address 'tmp'
  1214.  
  1215.     if rc>0 then    ret = 'Sysop'
  1216.     else                    ret = tmp.sysop
  1217.  
  1218. return ret
  1219.  
  1220.  
  1221. Get_Node: procedure
  1222.  
  1223.     parse arg left '.' . '@' right
  1224.  
  1225. return left'.0@'right
  1226.  
  1227.  
  1228. Get_Nodelists: procedure Expose system.
  1229.  
  1230.     system.nodelists = '<Nodelist not available>'
  1231.  
  1232.     if Command(system.shownl '>'system.tmpfile)>0 then return
  1233.  
  1234.     MM_ReadStem system.tmpfile 'tmp2'
  1235.     if rc>0 then return
  1236.  
  1237.     tmp.    = 0
  1238.  
  1239.     do n=1 to tmp2.count-1
  1240.         MM_AddToStem 'tmp' 'tmp2.'n
  1241.     end
  1242.  
  1243.     MM_SortStem 'tmp'
  1244.  
  1245.     system.nodelists = ''
  1246.  
  1247.     do n=0 to tmp.count-1
  1248.         system.nodelists    = system.nodelists strip(translate(tmp.n, ' ', '9'x))','
  1249.     end
  1250.  
  1251.     system.nodelists    = upper(strip(system.nodelists, 'b', ', '))
  1252.  
  1253.     call delete(system.tmpfile)
  1254. return
  1255.  
  1256.  
  1257. Get_Sysop: procedure
  1258.  
  1259.     parse arg name, address
  1260.  
  1261.     if index(address, '.0@')=0 then return name
  1262.  
  1263. return Get_Name(address)
  1264.  
  1265.  
  1266. Get_SystemDatas: procedure Expose system.
  1267.  
  1268.     MM_GetSysop        'system.sysop'
  1269.     parse var system.sysop system.sysop_first system.sysop_sur
  1270.  
  1271.     MM_GetNodes   'system.nodes'
  1272.     do n=0 to system.nodes.count-1
  1273.         system.nodes    = system.nodes system.nodes.n
  1274.     end
  1275.     upper system.nodes
  1276.  
  1277.     MM_GetAddrs                'system.addresses'
  1278.     MM_SortAddresses    'system.addresses'
  1279.  
  1280.     system.alldomains    = ''
  1281.  
  1282.     do n=0 to system.addresses.count-1
  1283.         system.addresses = system.addresses system.addresses.n
  1284.  
  1285.         tmp = Get_AddrDomain(system.addresses.n)
  1286.         if find(system.alldomains, tmp)=0 then system.alldomains    = system.alldomains tmp
  1287.     end
  1288.     upper system.addresses
  1289.  
  1290.     system.date    = translate(delstr(date(), 8, 2), '-', ' ')
  1291.     system.time = time()
  1292.  
  1293. return
  1294.  
  1295.  
  1296. Get_SystemInfo: procedure Expose system. write.
  1297.  
  1298.     /* If you disable this routine, you are not allowed to use MM_StarTrack anymore! */
  1299.  
  1300.     call Add_Write(' 'system.prg.info)
  1301.     call Add_Write()
  1302.     call Add_Write()
  1303.     call Add_Write('205379736F7020202020203A'x system.sysop)
  1304.     call Add_Write()
  1305.     call Add_Write('20416464726573736573203A'x system.addresses.count)
  1306.     call Add_Write('20446F6D61696E732020203A'x words(system.domains))
  1307.     call Add_Write('204D61696C6172656173203A'x system.mtrx.count '('system.mailareas.all')')
  1308.     call Add_Write()
  1309.  
  1310.     MM_ReadStem system.prg.stats 'write.text' 'APPEND'
  1311.  
  1312.     call Add_Write()
  1313.     call Add_Write()
  1314.  
  1315.     lst = '633A6C697374'x
  1316.  
  1317.     if exists(lst) then
  1318.         do
  1319.             call Command(lst system.prg.script'#?' '>'system.tmpfile)
  1320.             MM_ReadStem system.tmpfile 'write.text' 'APPEND'
  1321.  
  1322.             call Add_Write()
  1323.             call Add_Write()
  1324.  
  1325.             call Command(lst system.prg.cfgpath '>'system.tmpfile)
  1326.             MM_ReadStem system.tmpfile 'write.text' 'APPEND'
  1327.  
  1328.             call delete(system.tmpfile)
  1329.         end
  1330.     else
  1331.         do
  1332.             parse value statef(system.prg.script)'0 0 0 0 0' with . size . . date .
  1333.             call Add_Write(' 'left(system.prg.script, 40) right(size, 8)'   'date('n', date, 'i'))
  1334.  
  1335.             parse value statef(system.prg.cfg)'0 0 0 0 0' with . size . . date .
  1336.             call Add_Write(' 'left(system.prg.cfg, 40) right(size, 8)'   'date('n', date, 'i'))
  1337.         end
  1338.  
  1339.     call Add_Write()
  1340.     call Add_Write()
  1341.     call Add_Write(' 'system.prg.info)
  1342.     call Add_Write()
  1343.     call Add_Write()
  1344.  
  1345. return
  1346.  
  1347.  
  1348. Get_Text: procedure Expose msg. system.
  1349.  
  1350.     arg lang, typ, text
  1351.  
  1352.     if lang=''    then lang = 'DEFAULT'
  1353.  
  1354.     ret = system.txt.lang.typ.text
  1355.     if ret~=0 then return translate(ret, '2227'x, 'FEFF'x)
  1356.  
  1357.     cfg    = '#'typ'_'text
  1358.  
  1359.     call Log('*** WARNING: Unable to get' cfg 'of language' lang'!!!')
  1360.     call Log('             -> Using default instead.',, 4)
  1361.  
  1362.     ret = system.txt.default.typ.text
  1363.     if ret~=0 then return Replace_Embedded(translate(ret, '2227'x, 'FEFF'x))
  1364.  
  1365.     if lang='DEFAULT' then file = system.prg.txtpfx'/'
  1366.     else                                     file = system.prg.txtpfx'.'lang'/'
  1367.  
  1368.     call Log('*** CFG-ERROR: Unable to get' file'Misc:' cfg'!!!')
  1369.  
  1370. return '*** ERROR DETECTED ***'
  1371.  
  1372.  
  1373. Get_Version: procedure
  1374.  
  1375.     parse arg mode
  1376.  
  1377.     parse value sourceline(3-mode) with . . ver .
  1378.     parse var ver tst 'ß' .
  1379.  
  1380.     if ~datatype(strip(tst, 'b', '/ce '), 'N') then
  1381.         if ~mode then ver = Get_Version(1)
  1382.         else exit 99
  1383.  
  1384. return ver
  1385.  
  1386.  
  1387. Header:
  1388.  
  1389.     call Log(, '/', 3)
  1390.     call Log('***' system.prg.id '***', '+')
  1391.     call Log(system.prg.shortcr,, 3)
  1392.     call Log(,,3)
  1393.  
  1394. return
  1395.  
  1396.  
  1397. Make_Valid: procedure Expose system.
  1398.  
  1399.     arg string
  1400.  
  1401. return translate(string, system.replace, system.invalid)
  1402.  
  1403.  
  1404. Init:
  1405.  
  1406.     system.                            = 0
  1407.  
  1408.     MM_GetCfgPaths                'system.mm'
  1409.     MM_Version                        'system.mm'
  1410.     MM_GetTaskPri                    'system.taskpri'
  1411.  
  1412.     call                                     pragma('p', system.taskpri)
  1413.     call                                     pragma('w', 'NULL')
  1414.  
  1415.     system.path.cfg            = 'MM:Config'
  1416.     system.prg.ver            = Get_Version(0)
  1417.     system.prg.name            = 'MM_StarTrack'
  1418.     system.prg.cfgpath    = system.path.cfg'/'system.prg.name'/'
  1419.     system.prg.txtpfx        = system.prg.cfgpath'Texts'
  1420.     system.prg.id                = system.prg.name 'v'system.prg.ver
  1421.     system.prg.pfx            = system.prg.cfgpath || system.prg.name'.'
  1422.     system.prg.cfg            = system.prg.pfx'cfg'
  1423.     system.prg.shortcr    = '28432920313939342D393620526F6265727420486F666D616E6E'x
  1424.     system.prg.cr                = system.prg.shortcr '323A323439302F313031352E30404669646F4E6574'x
  1425.     system.prg.script        = 'MM:Rexx/'system.prg.name'.rexx'
  1426.     system.prg.stats        = system.prg.pfx'Statistics'
  1427.     system.mm.logpre      = getclip('MM_LogPre')
  1428.     system.prg.logpre     = system.mm.logpre'|'
  1429.     call                                        setclip('MM_LogPre', system.prg.logpre)
  1430.     system.prg.loglevel    = 2
  1431.     system.statistics     = 'CROSSNET EMPTY ENCODED EXCLUDE FATT KILL LOOP NTD PROCESSED RMPDST',
  1432.                                                 'RMPSRC RRR SIZE TWIT UCHK UCNT UDAY UNKNDST UNKNSRC'
  1433.     system.statcnt            = words(system.statistics)*4
  1434.     system.tmpfile            = 'T:'system.prg.name'.tmp'
  1435.     system.invalid            = xrange('0'x, '@') || xrange('[', 'FF'x)
  1436.     system.replace            = copies('_', length(system.invalid))
  1437.     system.rmkludges        = '4D534749443A20444F4D41494E20494E544C20464D505420544F505420434852533A20434841525345543A'x
  1438.     system.log.linelen    = 80
  1439.     system.log.prelen        = 11
  1440.     system.log.txtlen        = system.log.linelen-system.log.prelen
  1441.     system.log.prefix   = copies('.', system.log.prelen-2)': '
  1442.     system.log.etypfx        = copies(' ', system.log.prelen)
  1443.  
  1444.     system.check.0                    = 'NAME'
  1445.     system.check.1                    = 'ADDR'
  1446.     system.check.2                = 'SUBJ'
  1447.     system.check.count            = 3
  1448.     system.full_check.0            = 'FROM'
  1449.     system.full_check.1            = 'FROMADDR'
  1450.     system.full_check.2            = 'TO'
  1451.     system.full_check.3            = 'TOADDR'
  1452.     system.full_check.4            = 'SUBJ'
  1453.     system.full_check.count    = 5
  1454.  
  1455.     call Include_Lib('rexxsupport')
  1456. return
  1457.  
  1458.  
  1459. Include_Lib: procedure Expose system.
  1460.  
  1461.     parse arg lib, prio
  1462.     if right(upper(lib), 8)~='.LIBRARY' then lib=lib'.library'
  1463.     if prio='' then prio=0
  1464.  
  1465.     if ~show('l', lib) then
  1466.         if ~addlib(lib, prio, -30, 0) then call Quit(20, 'Could not open' lib'!!!')
  1467. return
  1468.  
  1469.  
  1470. Insert_Text: procedure Expose msg. system.
  1471.  
  1472.     parse arg file, language
  1473.  
  1474.     call Read_File(file, language)
  1475.  
  1476.     do n=0 to txt.count-1
  1477.         MM_AddToStem 'msg.data.addtxt' 'txt.'n
  1478.   end
  1479.  
  1480. return
  1481.  
  1482.  
  1483. IOerr:
  1484.  
  1485.     signal off ioerr
  1486.  
  1487.     return_code        = 30
  1488.     error_code        = rc
  1489.     error_line    = sigl
  1490.     error_msg            = 'IO-error' rc 'at line' error_line '['errortext(rc)']')
  1491.     rc                        = 0
  1492. signal Exit
  1493.  
  1494.  
  1495. Log: procedure Expose system.
  1496.  
  1497.     parse arg text, pre, level
  1498.  
  1499.     if ~datatype(level, 'N') then level = system.prg.loglevel
  1500.  
  1501.     tmp        = word('PRG MM', (pre~='')+1)
  1502.     text    = system.tmp.logpre || pre' 'text
  1503.  
  1504.     MM_WriteLog 'text' level
  1505. return
  1506.  
  1507.  
  1508. Log_Msg: procedure Expose domain. msg. system.
  1509.  
  1510.     arg dmn
  1511.  
  1512.     if domain.dmn.log.file=0 | pos('of' system.prg.name, msg.data.subj)>0 then return
  1513.  
  1514.     call Add_Log('Imported',    date()'  'time(),    'Created',        msg.data.datum msg.data.time)
  1515.     call Add_Log('From User',    msg.data.from,        'From Addr',    msg.data.fromaddr)
  1516.     call Add_Log('To   User',    msg.data.to,            'To   Addr',    msg.data.toaddr)
  1517.  
  1518.     if domain.dmn.log.size then msg_size    = msg.data.text.size 'bytes'
  1519.     else                                                msg_size    = ''
  1520.  
  1521.     if domain.dmn.log.flags then
  1522.         do
  1523.             msg_flags    = ''
  1524.             tmp                = msg.data.origflags
  1525.  
  1526.             do while tmp>''
  1527.                 parse var tmp check tmp
  1528.                 if find('PVT CRASH HOLD RRR FATT', check)=0 then iterate
  1529.  
  1530.                 msg_flags = msg_flags check
  1531.             end
  1532.  
  1533.             msg_flags    = strip(msg_flags)
  1534.  
  1535.             if msg_flags=''    then msg_flags = '<none>'
  1536.         end
  1537.     else msg_flags = ''
  1538.  
  1539.     if domain.dmn.log.size | domain.dmn.log.flags then
  1540.         call Add_Log('Size', msg_size, 'Flags', msg_flags)
  1541.  
  1542.     if domain.dmn.log.subj then    call Format_Log('Subject', msg.data.subj)
  1543.  
  1544.     if domain.dmn.log.routing then
  1545.         do
  1546.             tmp        = msg.data.foot.count-1
  1547.             do tmp=tmp to 0 by -1 while strip(msg.data.foot.tmp, 'b', '010D'x' ')=''; end
  1548.             from  = Get_Addr_From_Via(msg.data.foot.tmp)
  1549.  
  1550.             if from='???' then
  1551.                 if find(msg.data.origflags, 'CRASH')>0 then from = msg.data.fromaddr
  1552.  
  1553.             call Add_Log('Routing', 'from' from /* 'to' '???'*/ )
  1554.         end
  1555.  
  1556.     tmp.via    = 'Via'
  1557.     tmp.np    = '<no via-lines present>'
  1558.  
  1559.     if domain.dmn.log.via.all then
  1560.         if msg.data.foot.count=0 then call Add_Log(tmp.via, tmp.np)
  1561.         else
  1562.             do n=0 to msg.data.foot.count-1
  1563.                 tmp.line    = subword(strip(msg.data.foot.n, 'b', '010D'x' '), 2)
  1564.                 if tmp.line='' then iterate
  1565.  
  1566.                 call Add_Log(tmp.via, tmp.line)
  1567.                 tmp.via = ''
  1568.             end
  1569.  
  1570.     if domain.dmn.log.via.addr then
  1571.         if msg.data.foot.count=0 then call Add_Log(tmp.via, tmp.np)
  1572.         else
  1573.             do
  1574.                 tmp = ''
  1575.  
  1576.                 do n=0 to msg.data.foot.count-1
  1577.                     tmp.line    = strip(msg.data.foot.n, 'b', '010D'x' ')
  1578.                     if tmp.line='' then iterate
  1579.  
  1580.                     address = Get_Addr_From_Via(tmp.line)
  1581.  
  1582.                     if find(upper(tmp), upper(address))=0 then tmp    = tmp address
  1583.                 end
  1584.  
  1585.                 if tmp~='' then call Format_Log(tmp.via, strip(tmp))
  1586.             end
  1587.  
  1588.     call Format_Log('Status', msg.status.0)
  1589.     do n=1 to msg.status.count-1
  1590.         call Format_Log(, msg.status.n)
  1591.     end
  1592.  
  1593.     call Add_Log()
  1594.     call Add_Log()
  1595.  
  1596.     tmp    = domain.dmn.log.file
  1597.     if pos('%d', tmp)>0 then tmp = replace(tmp, translate(date('o'), '-', '/'), '%d')
  1598.  
  1599.     call Log('   Writing log "'tmp'".',, 4)
  1600.  
  1601.     MM_WriteStem tmp 'msg.log' 'APPEND'
  1602.     if rc>0 then call Log('*** WARNING: Unable to write file "'tmp'"!!!')
  1603.  
  1604. return
  1605.  
  1606.  
  1607. Msg_Head: procedure Expose system. write.
  1608.  
  1609.     parse arg text
  1610.  
  1611.     write. = 0
  1612.  
  1613.     call Add_Write()
  1614.     call Add_Write()
  1615.     call Add_Write(' 'text)
  1616.     call Add_Write(' 'copies('-', length(text)))
  1617.     call Add_Write()
  1618.     call Add_Write()
  1619.  
  1620. return
  1621.  
  1622.  
  1623. Move_File: procedure Expose system.
  1624.  
  1625.     parse arg from, to
  1626.  
  1627.     if exists(to)    then MM_MoveFile to to
  1628.  
  1629.     MM_MoveFile from to
  1630.  
  1631.     if RC=0    then call Log('   -> File "'from'" moved to "'to'".',, 3)
  1632.     else
  1633.         do
  1634.             call Log('*** IO-ERROR: Unable to move "'from"' to '"to'"!')
  1635.             to    = from
  1636.         end
  1637.  
  1638. return to
  1639.  
  1640.  
  1641. Move_FATT: procedure Expose domain. msg. rc. system.
  1642.  
  1643.     arg area, dmn, own
  1644.  
  1645.     tmp            = upper(msg.data.subj)
  1646.     subject    = Search_FATT(area, dmn, msg.data.subj, own)
  1647.  
  1648.     if rc.fatt>0    then    msg.data.subj    = strip(BaseName(subject) || left(' !', 2*own))
  1649.     else
  1650.         do
  1651.             call Forward_Msg('FATT-NotFound', msg.data.fromlang, area, msg.data.from,    msg.data.fromaddr,,
  1652.                                                 Get_Text(msg.data.fromlang, 'SUBJ', 'FATT_NOTFOUND'),, 1)
  1653.             msg.data.subj    = subject
  1654.         end
  1655.  
  1656.     msg.changed        = max(msg.changed, tmp~=upper(msg.data.subj))
  1657.  
  1658. return
  1659.  
  1660.  
  1661. Move_Msg: procedure Expose system.
  1662.  
  1663.     parse arg old_area, nr, new_area, txt
  1664.  
  1665.     MM_ReadMsg  old_area nr 'tmp'
  1666.  
  1667.     tmp.flags    = tmp.flags 'HOLD SENT'
  1668.  
  1669.     if system.mm.release<445 then
  1670.         do
  1671.             tmp.file    = system.tmpfile
  1672.  
  1673.             if txt~='' then
  1674.                 do
  1675.                     txt.count    = 3
  1676.                     txt.0            = ''
  1677.                     txt.1            = '*** BAD-REASON:' txt '***'
  1678.                     txt.2            = ''
  1679.                     mode            = 'APPEND'
  1680.  
  1681.                     MM_WriteStem tmp.file 'txt'
  1682.                 end
  1683.             else mode = ''
  1684.  
  1685.             MM_WriteStem    tmp.file    'tmp.head'    mode
  1686.             MM_WriteStem    tmp.file    'tmp.text'    'APPEND'
  1687.             MM_WriteStem    tmp.file    'tmp.foot'    'APPEND'
  1688.             MM_WriteMsg        new_area    'tmp'
  1689.         end
  1690.     else
  1691.         do
  1692.             subjbak        = msg.subj
  1693.             tmp.flags    = tmp.flags '!IMP'
  1694.  
  1695.             if txt~=''    then    tmp.subj    = '['txt']' tmp.subj
  1696.  
  1697.             MM_EditMsg        old_area nr 'tmp'
  1698.             MM_MoveMsg        old_area nr new_area
  1699.  
  1700.             msg.subj    = subjbak
  1701.         end
  1702.  
  1703.     MM_DeleteMsg old_area nr
  1704.  
  1705.     call Log('   Moved old msg #'nr 'to' new_area'.',, 3)
  1706.  
  1707. return
  1708.  
  1709.  
  1710. Notify_Author: procedure Expose domain. msg. system. write.
  1711.  
  1712.     /* If you disable this routine, you are not allowed to use MM_StarTrack anymore! */
  1713.  
  1714.     parse arg subject
  1715.  
  1716.     a.0    = '526F6265727420486F666D616E6E'x
  1717.     a.1    = '33393A3137312F3130312E3140416D6967614E6574'x
  1718.     a.2    = '323A323439302F313031352E31404669646F4E6574'x
  1719.  
  1720.     select
  1721.         when pos('414D4947414E4554'x,    system.addresses)>0 then t = 1
  1722.         when pos('4649444F4E4554'x,        system.addresses)>0 then t = 2
  1723.  
  1724.         otherwise return
  1725.     end
  1726.  
  1727.     tmp = Get_AddrDomain(a.t)
  1728.     tmp = word('KILL', domain.tmp.delete.own=1)
  1729.  
  1730.     call Write_Msg('write', system.mtrx.area.0, a.0, a.t, subject, system.tmpfile, tmp)
  1731. return
  1732.  
  1733.  
  1734. Parse_Args: procedure Expose system.
  1735.  
  1736.     arg args
  1737.  
  1738.     system.forcecpl    = Get_Arg('CPLCFG', 0)
  1739.  
  1740.     if args~='' then signal Usage
  1741.  
  1742. return
  1743.  
  1744.  
  1745. Parse_MsgDatas: procedure Expose msg. system.
  1746.  
  1747.     arg mode
  1748.  
  1749.     parse var msg.data.from msg.data.from_first msg.data.from_sur
  1750.     parse var msg.data.to   msg.data.to_first   msg.data.to_sur
  1751.  
  1752.     msg.data.from_first    = strip(msg.data.from_first)
  1753.     msg.data.from_sur        = strip(msg.data.from_sur)
  1754.     msg.data.fromdomain    = Get_AddrDomain(msg.data.fromaddr)
  1755.     msg.data.fromlang        = Get_Language(msg.data.fromaddr)
  1756.     msg.data.fromnode        = upper(Get_Node(msg.data.fromaddr))
  1757.     msg.data.tonode            = upper(Get_Node(msg.data.toaddr  ))
  1758.     msg.data.to_first        = strip(msg.data.to_first)
  1759.     msg.data.to_sur            = strip(msg.data.to_sur)
  1760.     msg.data.todomain        = Get_AddrDomain(msg.data.toaddr)
  1761.     msg.data.tolang            = Get_Language(msg.data.toaddr)
  1762.     msg.data.u_toaddr        = upper(msg.data.toaddr)
  1763.     msg.link                        = find(system.nodes,     msg.data.u_toaddr)>0
  1764.     msg.sysop                        = find(system.addresses, msg.data.u_toaddr)>0
  1765.     msg.system                    = find(system.addresses, msg.data.tonode    )>0
  1766.  
  1767.     if msg.data.fromdomain=msg.data.todomain then    system.domain = msg.data.fromdomain
  1768.  
  1769. return
  1770.  
  1771.  
  1772. Path: procedure
  1773.  
  1774.     parse arg path .
  1775.     if right(path,1) ~= '/' & right(path,1) ~= ':' then path = path'/'
  1776. return path
  1777.  
  1778.  
  1779. Process_Msgs: procedure Expose domain. remap. system.
  1780.  
  1781.     parse arg area, system.addr
  1782.  
  1783.     call Log(' Checking mail-area "'area'"...',, 3)
  1784.  
  1785.     msgs.    = 0
  1786.     MM_SearchMsgs area 'msgs' '#?' '#?' '#?' 'IMP' '!SENT'
  1787.  
  1788.     if RC=4    then
  1789.         do
  1790.             call Log('*** ERROR: Unknown area "'area'"! ***',, 0)
  1791.             return
  1792.         end
  1793.  
  1794.     call Log(' ' msgs.count 'msgs to process.',, 3)
  1795.  
  1796.     if msgs.count=0 then return
  1797.  
  1798.     call Add_Clip(area)
  1799.  
  1800.     parse var system.addr . '@' system.domain .
  1801.  
  1802.     system.domain    = Make_Valid(system.domain)
  1803.     system.exp        = 1
  1804.     upper                        area
  1805.  
  1806.     if ~system.nostats & ~system.stats    then call Read_Stats
  1807.     if msgs.count>0                                            then call Add_Clean(area)
  1808.  
  1809.     do n=0 to msgs.count-1
  1810.         rc.            = 0    ;    rc.kill    = ''    ; rc.twit    = ''    ;    rc.encoded    = ''
  1811.  
  1812.         admn = Read_Msg(area, msgs.n)
  1813.  
  1814.         call Log('  Processing msg #'msgs.n 'from' msg.data.fromaddr 'to' msg.data.toaddr)
  1815.         call Count_Stat('PROCESSED')
  1816.  
  1817.         if Set_RC('ROBOT', Check_Robot(msg.data.to)) then
  1818.             do
  1819.                 call Log('   Msg by' msg.data.from 'is for' msg.data.to', skipped.')
  1820.                 call Count_Stat('NTD')
  1821.                 iterate n
  1822.             end
  1823.  
  1824.         if Set_RC('EMPTY', Check_Empty(admn, msg.data.toaddr)) then
  1825.             do
  1826.                 call Log('   Msg by' msg.data.from 'is empty, deleted.')
  1827.                 MM_DeleteMsg area msgs.n
  1828.                 call Count_Stat('EMPTY')
  1829.                 iterate n
  1830.             end
  1831.  
  1832.         if Check_Function('EXECUTE')~=''    then call Execute_Cmd(area, msgs.n)
  1833.  
  1834.         if Check_Function('FORWARD')~=''    then
  1835.             if Forward(area, msgs.n)                then iterate
  1836.  
  1837.         select
  1838.             when Set_RC('KILL',            Check_Kill())~=''    then call Bounce_Twit('Kill',    area, msgs.n, rc.kill)
  1839.  
  1840.             when Set_RC('TWIT',            Check_Twit())~=''    then call Bounce_Twit('Twit',    area, msgs.n, rc.twit)
  1841.  
  1842.             when Set_RC('ENCODED',    Check_Encoded(area, msgs.n, admn, msg.data.text.size))~=''    then
  1843.                 call Bounce_Twit('Encoded', area, msgs.n, rc.encoded)
  1844.  
  1845.             when Check_Function('EXCLUDE')~=''    then
  1846.                 do
  1847.                     call Log('   Msg by' msg.data.from 'to' msg.data.to 'found in exclude-list, skipped.')
  1848.                     call Count_Stat('EXCLUDE')
  1849.                 end
  1850.  
  1851.             otherwise
  1852.                 do
  1853.                     call Set_RC('REMAPFROM',    Remap('FROM', msg.data.fromaddr,    msg.data.from,    area))
  1854.                     call Set_RC('REMAPTO',        Remap('TO',        msg.data.toaddr,        msg.data.to,        area))
  1855.  
  1856.                     if Set_RC('SRC',    Check_Address('FROM',    msg.data.fromaddr)) |,
  1857.                          Set_RC('DST',    Check_Address('TO',        msg.data.toaddr))    then
  1858.                             if system.nodelists=0 then call Get_Nodelists()
  1859.  
  1860.                     select
  1861.                         when rc.src & rc.dst    then call Both_Unknown(  area, msgs.n)
  1862.                         when rc.dst                        then call Bounce_Mail(   area, msgs.n)
  1863.                         when rc.src                        then call Unknown_Sender(area, msgs.n, rc.encoded)
  1864.  
  1865.                         when Set_RC('CROSSNET', Check_CrossNet(admn))>0 then
  1866.                             call Cross_Net(area, msgs.n, rc.crossnet, rc.src)
  1867.  
  1868.                         when Set_RC('LOOP', Check_Loop(domain.admn.loop, area, msgs.n)) then
  1869.                             call Stop_Loop(area, msgs.n, rc.src)
  1870.  
  1871.                         otherwise
  1872.                             do
  1873.                                 if Set_RC('FATT',    Check_FATT(admn, msg.data.flags))=3 then
  1874.                                     call Stop_FATT(area, msgs.n, admn)
  1875.  
  1876.                                 if Set_RC('RRR',    Check_RRR(admn)) then call Send_Receipt(area, msgs.n)
  1877.  
  1878.                                 if ~msg.sysop then MM_AddToStem 'system.delete.'area 'msgs.'n
  1879.  
  1880.                                 if rc.fatt>0    then
  1881.                                     do
  1882.                                         own    = rc.fatt=1
  1883.  
  1884.                                         if words(msg.data.subj)>1 then call Route_Multiple_FATT(area, admn, msg.data.subj, own)
  1885.                                         call Move_FATT(area, admn, own)
  1886.                                     end
  1887.  
  1888.                                 call Log('   Msg ok.')
  1889.  
  1890.                                 if rc.remapfrom+rc.remapto+rc.rrr+(rc.fatt~=3)=0 then call Count_Stat('NTD')
  1891.  
  1892.                                 call Count_Stat('SIZE', (msg.data.text.size+512)%1024)
  1893.                             end
  1894.                     end
  1895.  
  1896.                     if msg.changed>0    then call Update_Msg(area, msgs.n)
  1897.  
  1898.                     msg.bad = (rc.crossnet>0 | rc.dst | rc.encoded~='' | rc.fatt=3 | rc.kill~='' | rc.loop | rc.src | rc.twit~='')
  1899.  
  1900.                     if domain.admn.log.all | (domain.admn.log.bad & msg.bad) then call Log_Msg(admn)
  1901.  
  1902.                     if domain.admn.delete.good & ~msg.bad    & rc.fatt~=3 then
  1903.                         do
  1904.                             tmp = msg.data.flags 'IMP KILL'
  1905.                             MM_EditMsgFlags area nr tmp
  1906.                         end
  1907.  
  1908.                     drop msg. rc.
  1909.             end
  1910.         end
  1911.     end
  1912.  
  1913.     if domain.admn.export & system.exp then MM_AddToStem 'system.export' 'area'
  1914. return
  1915.  
  1916.  
  1917. Quit:
  1918.  
  1919.     parse arg return_code, error_msg
  1920.  
  1921.     error_line    = 0
  1922.     rc                    = 0
  1923. signal Exit
  1924.  
  1925.  
  1926. Read_File: procedure Expose system. txt.
  1927.  
  1928.     parse arg file, language .
  1929.  
  1930.     lang.default    = system.prg.txtpfx'/'file
  1931.     lang.lang            = system.prg.txtpfx'.'language'/'file
  1932.     txt.                    = 0
  1933.  
  1934.     if language~='' then
  1935.         if ~exists(lang.lang) then
  1936.             do
  1937.                 call Log('*** IO-ERROR: Unable to open "'lang.lang'"!!!')
  1938.                 call Log('              -> Using default instead.',, 4)
  1939.  
  1940.                 file    = lang.default
  1941.             end
  1942.         else file = lang.lang
  1943.     else file        = lang.default
  1944.  
  1945.     MM_ReadStem file 'txt'
  1946.     if rc>0 then
  1947.         do
  1948.             call Log('*** IO-ERROR: Unable to open "'file'"!!!')
  1949.             file = ''
  1950.         end
  1951.  
  1952. return file
  1953.  
  1954.  
  1955. Read_Msg: procedure Expose msg. domain. system.
  1956.  
  1957.     arg area, nr
  1958.  
  1959.     msg.    = 0
  1960.  
  1961.     MM_ReadMsg area nr 'msg.data'
  1962.  
  1963.     call Adjust_Addresses
  1964.     call Parse_MsgDatas('READMSG')
  1965.  
  1966.     parse var msg.data.date    msg.data.datum '  ' msg.data.time
  1967.  
  1968.     msg.data.datum            = translate(msg.data.datum, '-', ' ')
  1969.     msg.data.file                = ''
  1970.     msg.data.origflags    = msg.data.flags
  1971.     msg.error                        = ''
  1972.     msg.status.0                = 'Nothing to do, msg ok.'
  1973.  
  1974.     if msg.data.text.size=0 then
  1975.         do n=0 to msg.data.text.count-1
  1976.             msg.data.text.size    = msg.data.text.size+length(msg.data.text.n)
  1977.         end
  1978.  
  1979. return system.domain
  1980.  
  1981.  
  1982. Read_Stats: procedure Expose system.
  1983.  
  1984.     call Log('  Reading statistics...',, 4)
  1985.  
  1986.     parse value statef(system.prg.stats) with . . . . . . . desc
  1987.     desc    = strip(desc)
  1988.  
  1989.     if Check_Hex(desc, system.statcnt) then
  1990.         do
  1991.             tmp = system.statistics
  1992.  
  1993.             do while tmp~=''
  1994.                 parse var tmp name tmp
  1995.                 if name='' then iterate
  1996.  
  1997.                 parse var desc 1 num 5 desc
  1998.  
  1999.                 system.stat.name    = x2d(num)
  2000.             end
  2001.         end
  2002.  
  2003.     if system.stat.uday=0 then system.stat.uday = date(i)
  2004.  
  2005.     system.stats    = 1
  2006. return
  2007.  
  2008.  
  2009. Remap: procedure Expose domain. msg. remap. system.
  2010.  
  2011.     parse arg type, address, username, area
  2012.  
  2013.     if remap.type.count=0 then return 0
  2014.  
  2015.     old_addr    = address                ;    new_addr    = address
  2016.     old_name    = username             ;    new_name    = username
  2017.     dom                = type'DOMAIN'    ; dmn                = msg.data.dom
  2018.     remapped    = 0                            ; upper                address username
  2019.  
  2020.     do n=0 to remap.type.count-1 while ~remapped
  2021.         mode    = Remap_GetMode(remap.type.n.addr.old, remap.type.n.addr.new.dmn~='',,
  2022.                                                     remap.type.n.name.old, remap.type.n.name.new)
  2023.  
  2024.         select
  2025.             when mode=0 then iterate
  2026.  
  2027.             when mode=1 then
  2028.                 if Check_Pattern(remap.type.n.name.old, new_name) then
  2029.                     new_name = remap.type.n.name.new
  2030.  
  2031.             when mode=2 then
  2032.                 if Check_Pattern(Replace(remap.type.n.addr.old, '#?', '*'), new_addr)    then
  2033.                     do
  2034.                         new_addr = remap.type.n.addr.new.dmn
  2035.  
  2036.                         if pos('*', new_addr)>0 then
  2037.                             new_addr = Resolve_Wildcard(old_addr, remap.type.n.addr.old, new_addr)
  2038.                     end
  2039.  
  2040.             when mode=3 then
  2041.                 if Check_Pattern(Replace(remap.type.n.addr.old, '#?', '*'), new_addr)    then
  2042.                     do
  2043.                         new_name = remap.type.n.name.new
  2044.  
  2045.                         if pos('*', new_addr)>0 then
  2046.                             new_addr = Resolve_Wildcard(old_addr, remap.type.n.addr.old, new_addr)
  2047.                     end
  2048.  
  2049.             when mode=4 then
  2050.                 if Check_Pattern(remap.type.n.name.old, old_name) then
  2051.                     do
  2052.                         new_addr = remap.type.n.addr.new.dmn
  2053.  
  2054.                         if pos('*', new_addr)>0 then
  2055.                             new_addr = Resolve_Wildcard(old_addr, remap.type.n.addr.old, new_addr)
  2056.  
  2057.                         if remap.type.n.name.new~='' then new_name = remap.type.n.name.new
  2058.                     end
  2059.  
  2060.             when mode=5 then
  2061.                 if Check_Pattern(Replace(remap.type.n.addr.old, '#?', '*'), old_addr)    &,
  2062.                 Check_Pattern(remap.type.n.name.old, old_name) then
  2063.                     do
  2064.                         new_addr = remap.type.n.addr.new.dmn
  2065.                         new_name = remap.type.n.name.new
  2066.  
  2067.                         if pos('*', new_addr)>0 then
  2068.                             new_addr = Resolve_Wildcard(old_addr, remap.type.n.addr.old, new_addr)
  2069.                     end
  2070.  
  2071.             otherwise iterate
  2072.         end
  2073.  
  2074.         remapped    = upper(old_addr)~=upper(new_addr) | upper(old_name)~=upper(new_name)
  2075.     end
  2076.  
  2077.     if ~remapped then return 0
  2078.  
  2079.     if type='FROM' then
  2080.         do
  2081.             file    = 'Src'
  2082.             info    = 'SOURCE'
  2083.             reply    = 0
  2084.             stat    = 'SRC'
  2085.         end
  2086.     else
  2087.         do
  2088.             file  = 'Dst'
  2089.             info    = 'DESTINATION'
  2090.             stat    = 'DST'
  2091.  
  2092.             if find(system.addresses, upper(new_addr))>0 then
  2093.                 do
  2094.                     call Del_Flag('DEL')
  2095.                     call Del_Flag('KILL')
  2096.                 end
  2097.         end
  2098.  
  2099.     call Count_Stat('RMP'stat)
  2100.  
  2101.     info_old    = old_addr
  2102.     info_new    = new_addr
  2103.  
  2104.     if old_name~=new_name then
  2105.         do
  2106.             info_old    = old_name '%' info_old
  2107.             info_new    = new_name '%' info_new
  2108.         end
  2109.  
  2110.     if remap.type.n.flags='' then info_flags = ''
  2111.     else
  2112.         do
  2113.       info_flags            = strip(msg.data.flags)
  2114.             msg.data.flags    = remap.type.n.flags
  2115.         end
  2116.  
  2117.     adr                        = type'ADDR'
  2118.     lang                     = type'LANG'
  2119.     msg.data.adr    = new_addr
  2120.     msg.data.type    = new_name
  2121.     info                    = info'-address remapped from' info_old 'to' info_new
  2122.  
  2123.     call Parse_MsgDatas('REMAP')
  2124.  
  2125.     if remap.n.addinfo=1 then
  2126.         do
  2127.             call Read_File('Remap_'file, msg.data.lang)
  2128.  
  2129.             do n=0 to txt.count-1
  2130.                 tmp    = Replace_Embedded(txt.n)
  2131.                 tmp = Replace(tmp, info_new, '%n')
  2132.                 tmp = Replace(tmp, info_old, '%o')
  2133.                 MM_AddToStem 'msg.data.addtxt' 'tmp'
  2134.             end
  2135.  
  2136.             msg.changed    = 2
  2137.             info                = info', info added'
  2138.         end
  2139.     else
  2140.         if Get_AddrDomain(old_addr)=Get_AddrDomain(new_addr)    then call Set_MsgChanged
  2141.         else msg.changed = 2
  2142.  
  2143.     if remap.n.reply=1 then
  2144.         do
  2145.             call Read_File('Remap_Reply', msg.data.fromlang)
  2146.  
  2147.             write.    = 0
  2148.  
  2149.             do n=0 to txt.count-1
  2150.                 tmp    = Replace_Embedded(txt.n)
  2151.                 tmp = Replace(tmp, info_new, '%n')
  2152.                 tmp = Replace(tmp, info_old, '%o')
  2153.                 call    Add_Write(tmp)
  2154.             end
  2155.  
  2156.             sysop = Get_Sysop(msg.data.from, msg.data.fromaddr)
  2157.             txt   = Get_Text(msg.data.fromlang, 'SUBJ', 'REMAP_REPLY')
  2158.  
  2159.             call Write_Msg('write', area, sysop, msg.data.fromaddr, txt, system.tmpfile)
  2160.  
  2161.             info = info', sender notified'
  2162.         end
  2163.  
  2164.     info    = info'.'
  2165.  
  2166.     call Add_Status(info)
  2167.     call Log(  '   'info)
  2168.  
  2169.     if info_flags~='' then
  2170.         do
  2171.             info_flags    = 'Flags changed from "'info_flags'" to "'msg.data.flags'".'
  2172.  
  2173.             call Log(  '   'info_flags)
  2174.             call Add_Status(info_flags)
  2175.         end
  2176.  
  2177. return 1
  2178.  
  2179.  
  2180. Remap_GetMode: procedure Expose system.
  2181.  
  2182.     parse arg oa, na, on, nn
  2183.  
  2184.     oa    = oa~=''; on    = on~=''; nn    = nn~=''
  2185.  
  2186.     if ~oa & na=0 &  on &  nn then return 1
  2187.     if  oa & na=1 & ~on & ~nn then return 2
  2188.   if  oa & na=0 &  on &  nn then return 3
  2189.   if ~oa & na>0 &  on       then return 4
  2190.   if  oa & na=1 &  on &  nn then return 5
  2191.  
  2192. return 0
  2193.  
  2194.  
  2195. Replace: procedure
  2196.  
  2197.     parse arg string, new, old
  2198.  
  2199.     do while index(string, old)~=0
  2200.         interpret "parse var string l '"old"' r"
  2201.         string = l || new || r
  2202.     end
  2203.  
  2204. return string
  2205.  
  2206.  
  2207.  
  2208. Replace_Embedded: procedure Expose msg. system.
  2209.  
  2210.     parse arg text
  2211.  
  2212.     if pos('%', text)=0 then return text
  2213.  
  2214.     text    = replace(text, msg.data.datum,                '%cd')
  2215.     text    = replace(text, msg.data.time,                '%ct')
  2216.     text    = replace(text, msg.data.fromaddr,        '%fa')
  2217.     text    = replace(text, msg.data.from_first,    '%ff')
  2218.     text    = replace(text, msg.data.from_sur,        '%fs')
  2219.     text    = replace(text, msg.data.from,                '%f' )
  2220.     text    = replace(text, msg.data.flags,                '%F' )
  2221.     text    = replace(text, system.date,                    '%id')
  2222.     text    = replace(text, system.time,                    '%it')
  2223.     text    = replace(text, system.nodelists,            '%nl')
  2224.     text    = replace(text, system.addr,                    '%sa')
  2225.     text    = replace(text, system.sysop_first,        '%sf')
  2226.     text    = replace(text, system.sysop_sur,            '%ss')
  2227.     text    = replace(text, system.sysop,                    '%s' )
  2228.     text    = replace(text, msg.data.subj,                '%S' )
  2229.     text    = replace(text, msg.data.toaddr,            '%ta')
  2230.     text    = replace(text, msg.data.to_first,        '%tf')
  2231.     text    = replace(text, msg.data.to_sur,            '%ts')
  2232.     text    = replace(text, msg.data.to,                    '%t' )
  2233.  
  2234. return text
  2235.  
  2236.  
  2237. Report_Error:
  2238.  
  2239.     /* If you disable this routine, you are not allowed to use MM_StarTrack anymore! */
  2240.  
  2241.     signal off break_c
  2242.     signal off break_d
  2243.     signal off break_e
  2244.     signal off break_f
  2245.     signal off halt
  2246.     signal off ioerr
  2247.     signal off syntax
  2248.  
  2249.     call Log(' An internal error happened! Error-analysis started!',, 0)
  2250.  
  2251.     call Msg_Head('4E6F74696669636174696F6E2061626F757420616E206572726F72206F66'x system.prg.id)
  2252.     call Add_Write('202A2A2A'x error_msg '2A2A2A'x)
  2253.     call Add_Write()
  2254.     call Add_Write(' 'right(error_line, 4, '0')':' strip(translate(sourceline(error_line), ' ', '9'x)))
  2255.     call Add_Write()
  2256.     call Add_Write()
  2257.     call Add_Write()
  2258.  
  2259.     call Get_SystemInfo()
  2260.  
  2261.     call delete(system.tmpfile)
  2262.  
  2263.     address command
  2264.  
  2265.     ver    = 'version >>'system.tmpfile 'full'
  2266.     ver
  2267.     ver '726578787379736C69622E6C696272617279'x
  2268.     ver '72657878737570706F72742E6C696272617279'x
  2269.     ver '72657878686F73742E6C696272617279'x
  2270.     ver '7379733A73797374656D2F726578786D617374'x
  2271.     ver    '6D75696D61737465722E6C696272617279'x
  2272.  
  2273.     address 'MAILMANAGER'
  2274.  
  2275.     MM_ReadStem system.tmpfile 'write.text' 'APPEND'
  2276.  
  2277.     call Add_Write()
  2278.  
  2279.     if msg.data.from~='MSG.DATA.FROM' then
  2280.         do
  2281.             call Add_Write()
  2282.             call Add_Write(' MSG.DATA.FROM       = "'msg.data.from'"')
  2283.             call Add_Write(' MSG.DATA.FROMADDR   = "'msg.data.fromaddr'"')
  2284.             call Add_Write(' MSG.DATA.TO         = "'msg.data.to'"')
  2285.             call Add_Write(' MSG.DATA.TOADDR     = "'msg.data.toaddr'"')
  2286.             call Add_Write(' MSG.DATA.SUBJ       = "'msg.data.subj'"')
  2287.             call Add_Write(' MSG.DATA.DATE       = "'msg.data.date'"')
  2288.             call Add_Write(' MSG.DATA.FLAGS      = "'msg.data.flags'"')
  2289.             call Add_Write(' MSG.DATA.HEAD.COUNT = "'msg.data.head.count'"')
  2290.             call Add_Write(' MSG.DATA.TEXT.COUNT = "'msg.data.text.count'"')
  2291.             call Add_Write(' MSG.DATA.FOOT.COUNT = "'msg.data.foot.count'"')
  2292.             call Add_Write()
  2293.         end
  2294.  
  2295.     call Add_Write()
  2296.  
  2297.     MM_ReadStem system.prg.cfg 'write.text' 'APPEND'
  2298.  
  2299.     tmp = '633A6C697374'x
  2300.  
  2301.     if exists(tmp) then
  2302.         do
  2303.             address command tmp '>'system.tmpfile system.prg.cfgpath 'dates'
  2304.             tmp = rc
  2305.  
  2306.             call Add_Write()
  2307.  
  2308.             MM_ReadStem system.tmpfile 'write' 'APPEND'
  2309.             call delete(system.tmpfile)
  2310.  
  2311.             if tmp~=0 then call Add_Write('RC =' tmp)
  2312.         end
  2313.  
  2314.     call Add_Write()
  2315.     call Add_Write()
  2316.  
  2317.     call Notify_Author('4572726F722D5265706F7274206F66'x system.prg.name)
  2318. return
  2319.  
  2320.  
  2321. Request_Choice: procedure Expose system.
  2322.  
  2323.     parse arg text, buttons, ret_vals
  2324.  
  2325.     title    = system.prg.name'-Requester'
  2326.     text    = translate(Replace(text, '0A'x, '\n'), '1b'x, '\')
  2327.  
  2328.     if length(text)<40 then text = center(text, 40)
  2329.  
  2330.     MM_Requester title 'text' 'buttons'
  2331.  
  2332.     if rc=0 then rc=words(ret_vals)
  2333.  
  2334. return compress(word(ret_vals, rc), '_')
  2335.  
  2336.  
  2337. Resolve_Wildcard: procedure
  2338.  
  2339.     parse arg address, from, to
  2340.  
  2341.     parse var address a.1 ':' a.2 '/' a.3 '.' a.4 '@' a.5
  2342.     parse var from        f.1 ':' f.2 '/' f.3 '.' f.4 '@' f.5
  2343.     parse var to            t.1 ':' t.2 '/' t.3 '.' t.4 '@' t.5
  2344.  
  2345.     do n=1 to 5
  2346.         wposf    = pos('*', f.n)
  2347.         wpost    = pos('*', t.n)
  2348.  
  2349.         select
  2350.             when wposf=0 & wpost=0    then r.n = t.n
  2351.             when wposf>0 & wpost=0    then r.n = f.n
  2352.             when wposf>0 & wpost>0    then
  2353.                 do
  2354.                     dpos    = compare(f.n, t.n)
  2355.  
  2356.                     if dpos=0    then tmp = ''
  2357.                     else                     tmp = substr(t.n, dpos, wpost-dpos)
  2358.  
  2359.                     r.n        = left(f.n, max(dpos-1, 0)) || tmp || substr(a.n, wposf)
  2360.                 end
  2361.             otherwise
  2362.                 do
  2363.                     call Log('*** WILDCARD-ERROR: Unable to resolve' f.n '->' t.n '('from '->' to')')
  2364.                     call Log('                    Remap NOT possible!', 3)
  2365.  
  2366.                     do m=1 to 5
  2367.                         r.m = a.m
  2368.                     end
  2369.  
  2370.                     leave n
  2371.                 end
  2372.         end
  2373.     end
  2374.  
  2375. return r.1':'r.2'/'r.3'.'r.4'@'r.5
  2376.  
  2377.  
  2378. Route_Multiple_FATT: procedure Expose domain. msg. msgs. rc. system.
  2379.  
  2380.     parse arg area, admn, first_file additional_files, own
  2381.  
  2382.     call Log('   Multiple FATT detected, writing additional msgs...')
  2383.  
  2384.     call Read_File('FATT-Multiple', msg.data.tolang)
  2385.  
  2386.   file_bak                = msg.data.file
  2387.     flags_bak                = msg.data.flags
  2388.     msg.data.file        = system.tmpfile'2'
  2389.  
  2390.     if domain.admn.delete.own    then msg.data.flags = msg.data.flags 'KILL'
  2391.  
  2392.     do n=0 to txt.count-1
  2393.         txt.n    = Replace_Embedded(txt.n)
  2394.     end
  2395.  
  2396.     MM_AddToStem        'txt'                    'system.prg.tearline'
  2397.     MM_WriteStem        msg.data.file    'txt'
  2398.  
  2399.     do while additional_files~=''
  2400.         parse var additional_files filename additional_files
  2401.         if filename='' then iterate
  2402.  
  2403.         if ~own then call Log('   -> Generating new msg for "'filename'".')
  2404.  
  2405.         msg.data.subj    = filename
  2406.         call Move_FATT(area, admn, own)
  2407.  
  2408.         if ~own then MM_WriteMsg area 'msg.data'
  2409.     end
  2410.  
  2411.     call delete(msg.data.file)
  2412.  
  2413.     msg.data.file        = file_bak
  2414.     msg.data.flags    = flags_bak
  2415.     msg.data.subj        = first_file
  2416.     msg.changed            = max(msg.changed, 1)
  2417.  
  2418. return
  2419.  
  2420.  
  2421. Search_FATT: procedure Expose domain. msg. rc. system.
  2422.  
  2423.     parse arg area, dmn, name, own
  2424.     upper dmn
  2425.  
  2426.     if own & domain.dmn.fatt.ownpath~=''    then    mailpath    = domain.dmn.fatt.ownpath
  2427.     else
  2428.         do
  2429.             tmp                    = Make_Valid(area)
  2430.             mailpath        = system.mtrx.path.tmp
  2431.         end
  2432.  
  2433.     check.        = 0
  2434.     MM_AddToStem 'check' 'mailpath'
  2435.     MM_AddToStem 'check' 'system.mm.inbound'
  2436.     MM_AddToStem 'check' 'system.mm.baddir'
  2437.     MM_AddToStem 'check' 'system.badpath'
  2438.  
  2439.     name    = BaseName(name)
  2440.  
  2441.     if domain.dmn.fatt.adjust & pos(',', name)>0 then parse var name name ',' ext
  2442.  
  2443.     found = 0
  2444.  
  2445.     do n=0 to check.count-1 while ~found
  2446.         path    = check.n
  2447.         file    = path || name
  2448.         found    = exists(file)
  2449.  
  2450.         if ~domain.dmn.fatt.adjust then iterate
  2451.  
  2452.         call Command('c:list >'system.tmpfile file',#? lformat "%p%n"')
  2453.         MM_ReadStem system.tmpfile 'list'
  2454.  
  2455.         if list.count>0 & exists(list.0) then
  2456.             do
  2457.                 do m=0+(~found) to list.count-1
  2458.                     call Log('   -> Deleting "'list.m'"...',, 4)
  2459.                     call delete(list.m)
  2460.                 end
  2461.  
  2462.                 if found                                    then break
  2463.  
  2464.                 if rename(list.0, file)    then call Log('   -> "'list.0'" renamed to "'file'".',, 3)
  2465.                 else
  2466.                     do
  2467.                         call Log('*** IO-ERROR: Unable to rename "'list.0'" to "'file'"!')
  2468.  
  2469.                         file    = list.0
  2470.                         name    = BaseName(file)
  2471.                     end
  2472.  
  2473.                 found    = 1
  2474.             end
  2475.     end
  2476.  
  2477.     call delete(system.tmpfile)
  2478.  
  2479.     if found    then
  2480.         do
  2481.             if upper(path)~=upper(mailpath) then file = Move_File(file, mailpath || name)
  2482.  
  2483.             parse value statef(file)'FF'x' ?' with . msg.fatt.size . . . . . desc 'FF'x .
  2484.             desc    = 'FATT: From' msg.data.fromaddr 'to' msg.data.toaddr';' desc
  2485.  
  2486.             MM_SetFilenote file 'desc'
  2487.         end
  2488.     else
  2489.         do
  2490.             call Log('   -> Unable to locate file "'name'"!!!')
  2491.  
  2492.             file                = Replace_Embedded(replace(Get_Text(msg.data.tolang, 'SUBJ', 'NOFATT'), name, '%fatt'))
  2493.             rc.fatt            = 0
  2494.             rc.was_fatt    = 1
  2495.             call                    Del_Flag('FATT')
  2496.         end
  2497.  
  2498. return file
  2499.  
  2500.  
  2501. Send_Receipt: procedure Expose domain. msg. system.
  2502.  
  2503.     parse arg area, nr
  2504.  
  2505.     call Log('   SENDING RECEIPT!!!')
  2506.  
  2507.     call Forward_Msg('ReturnReceiptRequest', msg.data.fromlang, area, msg.data.from,,
  2508.                     msg.data.fromaddr, Get_Text(msg.data.fromlang, 'SUBJ', 'RRR'),, 1)
  2509.  
  2510.     call Add_Status('Returned Receipt-Request.')
  2511.     call Count_Stat('RRR')
  2512. return
  2513.  
  2514.  
  2515. Set_MsgChanged: procedure Expose msg. system.
  2516.  
  2517.     msg.changed    = max(msg.changed, 1+(system.mm.release<421))
  2518.  
  2519. return
  2520.  
  2521.  
  2522. Set_RC: procedure Expose rc. system.
  2523.  
  2524.     parse arg type, ret
  2525.  
  2526.     if rc.type~='' & rc.type~=0    then
  2527.         if datatype(rc.type, 'N')    then    rc.type = max(rc.type,    ret)
  2528.         else                                                        rc.type = strip(rc.type ret)
  2529.     else                                                            rc.type    = ret
  2530.  
  2531.     call Debug('RC', type':' rc.type)
  2532.  
  2533. return rc.type
  2534.  
  2535.  
  2536. Stop_FATT: procedure Expose msg. domain. system. rc.
  2537.  
  2538.     arg area, nr, dmn
  2539.  
  2540.     call Log('   FILEATTACH!!! From' msg.data.from 'to' msg.data.to)
  2541.  
  2542.     msg.data.subj    = BaseName(strip(msg.data.subj))
  2543.     fatt.file            = Search_FATT(area, dmn, msg.data.subj, 0)
  2544.  
  2545.     if rc.fatt=0    then return
  2546.  
  2547.     notify            = domain.dmn.fatt.notify
  2548.     found                = 0
  2549.     rc.fatt            = 0
  2550.     rc.was_fatt    = 1
  2551.     call                    Del_Flag('FATT')
  2552.  
  2553.     select
  2554.         when domain.dmn.fatt.hold then
  2555.             do
  2556.                 notify_txt    = 'put on hold'
  2557.  
  2558.                 msg.data.subj    = msg.data.subj',' msg.fatt.size 'bytes'
  2559.  
  2560.                 parse var msg.data.toaddr zone ':' net '/' node '.' point '@' .
  2561.  
  2562.                 flow = system.mm.outbound || zone'.'net'.'node'.'point'.HLO'
  2563.                 open = 1
  2564.  
  2565.                 if ~open(out, flow, a) then
  2566.                     if ~open(out, flow, w) then
  2567.                         if ~open(out, flow'_TMP', a) then
  2568.                             if ~open(out, flow'_TMP', w) then
  2569.                                 do
  2570.                                     call Log('*** IO-ERROR: Unable to create flow-file' flow'!!!')
  2571.                                     open        = 0
  2572.                                     notify    = 0
  2573.                                 end
  2574.  
  2575.                 if open then
  2576.                     do
  2577.                         call writeln(out, '^'fatt.file)
  2578.                         call   close(out)
  2579.  
  2580.                         call Log('   -> File' fatt.file 'was put on hold for' msg.data.toaddr'.')
  2581.                     end
  2582.             end
  2583.  
  2584.         when domain.dmn.fatt.bad then
  2585.             do
  2586.                 notify_txt    = 'moved to the #BADDIR'
  2587.                 fatt.file        = Move_File(fatt.file, system.mm.baddir || BaseName(fatt.file))
  2588.             end
  2589.  
  2590.         otherwise
  2591.             do
  2592.                 notify_txt    = 'deleted'
  2593.  
  2594.                 if delete(fatt.file) then    call Log('   -> File' fatt.file 'was deleted.')
  2595.                 else                                            call Log('*** IO-ERROR: Unable to delete "'fatt.file'"!')
  2596.             end
  2597.     end
  2598.  
  2599.     if notify then
  2600.         do
  2601.             tmp = word('Killed Hold', domain.dmn.fatt.hold+1)
  2602.  
  2603.             call Insert_Text('FATT-'tmp'_ToDst', msg.data.tolang  )
  2604.             call Read_File(  'FATT-'tmp'_ToSrc', msg.data.fromlang)
  2605.  
  2606.             write.    = 0
  2607.             do n=0 to txt.count-1
  2608.                 call Add_Write(Replace_Embedded(txt.n))
  2609.             end
  2610.  
  2611.             sysop = Get_Sysop(msg.data.from, msg.data.fromaddr)
  2612.             txt   = Get_Text(msg.data.fromlang, 'SUBJ', 'FATT_TOSRC')
  2613.  
  2614.             call Write_Msg('write', area, sysop, msg.data.fromaddr, txt, system.tmpfile)
  2615.  
  2616.             info = 'File was' notify_txt', source & destination were notified.'
  2617.         end
  2618.     else info = ''
  2619.  
  2620.     call Add_Status('FILEATTACH stopped!!!' info)
  2621.     call Count_Stat('FATT')
  2622.  
  2623. return
  2624.  
  2625.  
  2626. Stop_Loop: procedure Expose domain. msg. system.
  2627.  
  2628.     parse arg area, nr, src
  2629.  
  2630.     call Log('   NETMAIL-LOOP!!!')
  2631.  
  2632.     tmp            = msg.data.foot.count-1
  2633.     dstaddr    = Get_Addr_From_Via(msg.data.foot.tmp)
  2634.  
  2635.     if dstaddr='???' then dstaddr = msg.data.toaddr
  2636.  
  2637.     dstname    = Get_Name(         dstaddr)
  2638.     dstlang    = Get_Language(dstaddr)
  2639.  
  2640.     if dstaddr~=msg.data.toaddr & dstname~=msg.data.to then
  2641.         do
  2642.             if ~msg.system then
  2643.                 do
  2644.                     flg = 'CRASH'
  2645.                     txt    = flg'ED'
  2646.                 end
  2647.             else
  2648.                 do
  2649.                     flg = ''
  2650.                     txt    = 'SENT'
  2651.                 end
  2652.  
  2653.             call Forward_Msg('Loop_ToDst', msg.data.tolang, area, msg.data.to, msg.data.toaddr,,
  2654.                                                 Get_Text(msg.data.tolang, 'SUBJ', 'LOOP_TODST'), flg, 0)
  2655.  
  2656.             msg.data.text.0            = ''
  2657.             msg.data.text.1            = '[...]'
  2658.             msg.data.text.2            = ''
  2659.             msg.data.text.count = 3
  2660.             crashed                            = '***' txt 'TO DESTINATION *** '
  2661.         end
  2662.     else crashed = ''
  2663.  
  2664.     call Forward_Msg('Loop_ToLink', dstlang, area, dstname, dstaddr,,
  2665.                                         Get_Text(Get_Language(dstaddr), 'SUBJ', 'LOOP_TOLINK'),, 0)
  2666.  
  2667.     if ~src then
  2668.         do
  2669.             call Forward_Msg('Loop_ToSrc', msg.data.fromlang, area, msg.data.from, msg.data.fromaddr,,
  2670.                                                 Get_Text(msg.data.fromlang, 'SUBJ', 'LOOP_TOSRC'),, 1)
  2671.  
  2672.             resend = 'Msg resend.'
  2673.         end
  2674.     else resend = ''
  2675.  
  2676.     call Move_Msg(area, nr, system.badarea, 'Loop-mail')
  2677.  
  2678.     call Add_Status(strip('Netmail-loop detected! 'crashed || resend))
  2679.     call Count_Stat('LOOP')
  2680. return 1
  2681.  
  2682.  
  2683. Syntax:
  2684.  
  2685.     signal off syntax
  2686.  
  2687.     return_code        = 40
  2688.     error_code        = rc
  2689.     error_line        = sigl
  2690.     error_msg            = 'Error' rc 'at line' error_line '['errortext(rc)']'
  2691.     rc                        = 0
  2692. signal Exit
  2693.  
  2694.  
  2695. Unknown_Sender: procedure Expose domain. msg. system.
  2696.  
  2697.     parse arg area, nr, encoded
  2698.  
  2699.     if encoded~='' then call Cut_Text(10)
  2700.  
  2701.     call Log('   UNKNOWN SENDER!!!')
  2702.  
  2703.     call Forward_Msg('Unknown_Source', msg.data.tolang, area, msg.data.to, msg.data.toaddr,,
  2704.                                         Get_Text(msg.data.tolang, 'SUBJ', 'UNKNSRC'),, 0)
  2705.  
  2706.     call Move_Msg(area, nr, system.badarea, 'Unknown sender')
  2707.  
  2708.     call Add_Status('Unknown SOURCE-address detected:' msg.data.fromaddr', msg forwarded.')
  2709.     call Count_Stat('UNKNSRC')
  2710. return
  2711.  
  2712.  
  2713. Update_Msg: procedure Expose msg. system.
  2714.  
  2715.     parse arg area, nr
  2716.  
  2717.     if msg.changed=2    then
  2718.         do
  2719.             call Adjust_Kludges()
  2720.             call Add_Kludge(0, 'ORIGDATE:' msg.data.date)
  2721.  
  2722.             msg.data.file        = system.tmpfile
  2723.             msg.data.flags    = msg.data.flags 'IMP'
  2724.  
  2725.             call Write_Msg_File('msg.data', system.tmpfile, 0, 1)
  2726.         end
  2727.  
  2728.     MM_EditMsg area nr 'msg.data'
  2729.  
  2730.     if msg.changed=2 then call delete(system.tmpfile)
  2731. return
  2732.  
  2733.  
  2734. Usage:
  2735.  
  2736.     say
  2737.     say 'Usage: [RX] MM_StarTrack[.rexx] [CPLCFG]'
  2738.     say
  2739.  
  2740. call Quit(0, 'Usage requested')
  2741.  
  2742.  
  2743. Wait_AreasWindow: procedure Expose system.
  2744.  
  2745.     MM_AreasWin
  2746.     if rc=0 then return
  2747.  
  2748.     bell    = '07'x
  2749.     cr        = '0D'x
  2750.  
  2751.     call Request_Choice('\c\n\1'system.prg.id'\0 is waiting.\n\nPlease go back to the Areas-Window',
  2752.                                             'as soon as possible!\n', '* _WAIT ', '_')
  2753.  
  2754.     tmp        = 'Waiting for Areas-Window...'
  2755.     call writech(STDOUT, bell || tmp || cr)
  2756.     call Log(tmp,, 4)
  2757.  
  2758.     rc    = 1
  2759.  
  2760.     do while rc~=0
  2761.         MM_AreasWin
  2762.  
  2763.         call writech(STDOUT, bell)
  2764.         call Delay(250)
  2765.     end
  2766.  
  2767. return
  2768.  
  2769.  
  2770. Write_Msg: procedure Expose domain. msg. system. write.
  2771.  
  2772.     starmsg. = 0
  2773.     parse arg stem, area, starmsg.data.to, starmsg.data.toaddr, starmsg.data.subj, starmsg.data.file, starmsg.data.flags
  2774.  
  2775.     MM_GetAreaInfo area 'ainfo'
  2776.     is_matrix    = ainfo.type='MAIL'
  2777.  
  2778.     if is_matrix then
  2779.         do
  2780.             tmp = Check_Addr(starmsg.data.toaddr, 'ADJUST')
  2781.             MM_GetNearestAddr tmp 'tmp_addr'
  2782.             if rc~=0 then tmp_addr = system.addresses.0
  2783.  
  2784.             tmp                                        = Get_AddrDomain(tmp_addr)
  2785.             starmsg.data.fromaddr    = domain.tmp.replyaddr
  2786.  
  2787.             if starmsg.data.fromaddr=0    then starmsg.data.fromaddr = tmp_addr
  2788.         end
  2789.     else
  2790.         do
  2791.             starmsg.data.tear        = system.prg.fid
  2792.             starmsg.data.origin    = translate(system.prg.cr, '[]', '()')
  2793.         end
  2794.  
  2795.     starmsg.data.from    = system.prg.fid
  2796.  
  2797.     if domain.tmp.delete.own=1    then
  2798.         if find(starmsg.data.flags, 'KILL')=0 then starmsg.data.flags = starmsg.data.flags 'KILL'
  2799.  
  2800.     call Add_Kludge(1, 'ROBOTMAIL')
  2801.  
  2802.     call Write_Msg_File(stem, starmsg.data.file, is_matrix, is_matrix)
  2803.  
  2804.     MM_WriteMsg area 'starmsg.data'
  2805.     if rc>0 then call Log('*** MM-ERROR: Unable to write a new msg in' area'!!!')
  2806.  
  2807.     call delete(starmsg.data.file)
  2808.  
  2809.     drop starmsg. write.
  2810. return
  2811.  
  2812.  
  2813. Write_Msg_File: procedure Expose msg. system. write.
  2814.  
  2815.     parse arg stem, file, tear, via
  2816.  
  2817.     if tear    then MM_AddToStem stem'.text' 'system.prg.tearline'
  2818.     if via    then call Add_Via(stem'.foot')
  2819.  
  2820.     MM_WriteStem file stem'.head'                            ; ret = rc
  2821.     MM_WriteStem file stem'.addtxt'    'APPEND'    ; ret = ret+abs(rc)
  2822.     MM_WriteStem file stem'.text'      'APPEND'    ; ret = ret+abs(rc)
  2823.     MM_WriteStem file stem'.foot'        'APPEND'    ; ret = ret+abs(rc)
  2824.  
  2825.     if ret>0 then call Log('*** IO-ERROR: Troubles while writing file "'file'"!!!')
  2826.  
  2827. return
  2828.  
  2829.  
  2830. Write_Stats: procedure Expose domain. system.
  2831.  
  2832.     call Check_Processed
  2833.     call Log(' Writing statistics...',, 4)
  2834.  
  2835.     line                = '   'copies('-', 30)
  2836.     statistic.    = 0
  2837.     tmp                    = 'Netmail-Statistics of' system.prg.id':'
  2838.     total.err        = system.stat.crossnet+system.stat.empty+system.stat.encoded+system.stat.exclude,
  2839.                                 +system.stat.kill+system.stat.loop+system.stat.twit+system.stat.unkndst,
  2840.                                 +system.stat.unknsrc
  2841.     total.ok        = system.stat.ntd+system.stat.rmpsrc+system.stat.rmpdst+system.stat.rrr
  2842.  
  2843.     call Add_Stat()
  2844.     call Add_Stat()
  2845.     call Add_Stat(' 'tmp)
  2846.     call Add_Stat(' 'copies('=', length(tmp)))
  2847.     call Add_Stat('   (since' translate(date('n', system.stat.uday, 'i'),'-', ' ')')')
  2848.     call Add_Stat()
  2849.     call Add_Stat()
  2850.     call Add_Stat('CrossNet mails',                    system.stat.crossnet)
  2851.     call Add_Stat('Empty mails',                        system.stat.empty)
  2852.     call Add_Stat('Encoded mails',                    system.stat.encoded)
  2853.     call Add_Stat('Excluded mails',                    system.stat.exclude)
  2854.     call Add_Stat('Fileattaches',                        system.stat.fatt)
  2855.     call Add_Stat('Killed mails',                        system.stat.kill)
  2856.     call Add_Stat('Loop mails',                            system.stat.loop)
  2857.     call Add_Stat('Twitted mails',                    system.stat.twit)
  2858.     call Add_Stat('Unknown destinations',        system.stat.unkndst)
  2859.     call Add_Stat('Unknown senders',                system.stat.unknsrc)
  2860.     call Add_Stat(line)
  2861.     call Add_Stat('Mails with errors etc',    total.err)
  2862.     call Add_Stat()
  2863.     call Add_Stat()
  2864.     call Add_Stat('Remapped source-addr',        system.stat.rmpsrc)
  2865.     call Add_Stat('Remapped dest.-addr',        system.stat.rmpdst)
  2866.     call Add_Stat("Returned receipt's",            system.stat.rrr)
  2867.     call Add_Stat('Nothing to do',                    system.stat.ntd)
  2868.     call Add_Stat(line)
  2869.     call Add_Stat('Mails without errors',        total.ok)
  2870.     call Add_Stat()
  2871.     call Add_Stat()
  2872.     call Add_Stat('   'copies('=', 30))
  2873.     call Add_Stat('Total mails processed',    system.stat.processed)
  2874.     call Add_Stat('Total kbytes routed',        system.stat.size)
  2875.     call Add_Stat()
  2876.     call Add_Stat('Times program used',            system.stat.ucnt)
  2877.     call Add_Stat()
  2878.     call Add_Stat()
  2879.  
  2880.     tmp        = system.statistics
  2881.     desc    = ''
  2882.  
  2883.     do while tmp~=''
  2884.         parse var tmp name tmp
  2885.         if name='' then iterate
  2886.  
  2887.         desc    = desc || d2h(system.stat.name)
  2888.     end
  2889.  
  2890.     desc    = desc d2x(hash(desc))
  2891.  
  2892.     MM_WriteStem        system.prg.stats 'statistic'
  2893.     call delay(25)
  2894.     MM_SetFileNote    system.prg.stats 'desc'
  2895.  
  2896. return
  2897.  
  2898.  
  2899.  
  2900.                 /********** Config related routines **********/
  2901.  
  2902.  
  2903.  
  2904.  
  2905. Add_AllowEncoded: procedure Expose cpl. domain. system.
  2906.  
  2907.     arg dmn, mode, var, patterns
  2908.  
  2909.     do while patterns~=''
  2910.         parse var patterns ptrn patterns
  2911.         if strip(ptrn)='' then iterate
  2912.  
  2913.         call Add_Cfg_Stem(var'.encoded.'mode, Replace(ptrn, '#?', '*'))
  2914.     end
  2915.  
  2916.     domain.dmn.fkt.allowenc    = 1
  2917.  
  2918. return
  2919.  
  2920.  
  2921. Add_Cfg: procedure Expose cpl. domain. remap. system.
  2922.  
  2923.     parse arg var, val, add, force
  2924.     upper var
  2925.  
  2926.     if force~=1 & val=0    then return
  2927.  
  2928.     if datatype(val, 'N') & add=''    then    q        = ''
  2929.     else                                                                    q        = "'"
  2930.  
  2931.     line    = var'='q || translate(compress(val, '000A0D'x), 'FEFF'x, '2227'x) || q || add
  2932.  
  2933.     MM_AddToStem 'cpl' 'line'
  2934.  
  2935.     interpret line
  2936.  
  2937. return
  2938.  
  2939.  
  2940. Add_Cfg_Stem: procedure Expose cpl. domain. remap. system.
  2941.  
  2942.     parse arg stem, value
  2943.     upper stem
  2944.  
  2945.     MM_AddToStem stem 'value'
  2946.  
  2947.     if find(cpl.stems, stem)=0 then cpl.stems = cpl.stems stem
  2948.  
  2949. return
  2950.  
  2951.  
  2952. Add_Full_Pattern: procedure Expose cfg. cpl. n system.
  2953.  
  2954.     parse arg type, args
  2955.  
  2956.     call Parse_Cfg_Args(upper(args), 'FROM_PATTERN/A,FROMADDR_PATTERN/A,TO_PATTERN/A,TOADDR_PATTERN/A,SUBJ_PATTERN/A', '#'type, l)
  2957.  
  2958.     args    = ''
  2959.     uargs    = ''
  2960.     found = find('EXCLUDE KILL TWIT', type)>0
  2961.     key_l    = n+1
  2962.  
  2963.     do n=n+1 to cfg.count-1
  2964.         call Parse_Cfg_Line(n)
  2965.  
  2966.         if key=''    then iterate
  2967.  
  2968.         if key='ARGUMENTS' then
  2969.             if type='EXCLUDE'    then call Quit(15, 'You must not use "Arguments" for #EXCLUDE at line' l'!!!')
  2970.             else found    = 1
  2971.         else
  2972.             do
  2973.                 n            = n-1
  2974.                 args    = ''
  2975.                 uargs    = ''
  2976.             end
  2977.  
  2978.         leave
  2979.     end
  2980.  
  2981.     if ~found then call Quit(15, 'You have to set "Arguments" for #'type 'at line' key_l'!!!')
  2982.  
  2983.     tmp    = 'system.ptrn.'type'.'
  2984.  
  2985.     call Add_Cfg_Stem(tmp'from',             Replace(cfg.prm.from_pattern,            '#?', '*'))
  2986.     call Add_Cfg_Stem(tmp'fromaddr',    Replace(cfg.prm.fromaddr_pattern, '#?', '*'))
  2987.     call Add_Cfg_Stem(tmp'to',                Replace(cfg.prm.to_pattern,                '#?', '*'))
  2988.     call Add_Cfg_Stem(tmp'toaddr',        Replace(cfg.prm.toaddr_pattern,        '#?', '*'))
  2989.     call Add_Cfg_Stem(tmp'subj',            Replace(cfg.prm.subj_pattern,            '#?', '*'))
  2990.  
  2991.     select
  2992.         when type='EXCLUDE'    then call Add_Cfg_Stem(tmp'mode', '*')
  2993.         when type='EXECUTE'    then call Add_Cfg_Stem(tmp'mode', args)
  2994.         when type='FORWARD'    then call Get_ForwardDatas(tmp, args, '#FORWARD-"Arguments"', l)
  2995.  
  2996.         otherwise
  2997.             do
  2998.                 if args~=''    then call Parse_Cfg_Args(args, 'BOUNCE/S,MOVE/S', '#'type'-"Arguments"', l)
  2999.                 call Add_Cfg_Stem(tmp'mode', '*' uargs)
  3000.             end
  3001.     end
  3002.  
  3003.     system.fkt.fp.type    = 1
  3004. return
  3005.  
  3006.  
  3007. Add_Matrix: procedure Expose cpl. system.
  3008.  
  3009.     parse arg areaname, addr, pth
  3010.  
  3011.     tmp     = 'system.mtrx.'
  3012.  
  3013.     call Add_Cfg_Stem(tmp'area',                                    areaname)
  3014.     call Add_Cfg_Stem(tmp'addr',                                    addr)
  3015.     call Add_Cfg(tmp'path.'Make_Valid(areaname),    pth)
  3016.  
  3017.     system.mtrx.count    = system.mtrx.area.count
  3018. return
  3019.  
  3020.  
  3021. Add_Pattern: procedure Expose cpl. system.
  3022.  
  3023.     parse arg type, stem, pt md, l
  3024.     upper            type    stem    pt
  3025.     pt    = strip(pt)
  3026.     md    = strip(md)
  3027.  
  3028.     if find('EXECUTE FORWARD', type)=0    then md = upper(md)
  3029.     if type='EXCLUDE'    | md=''                     then md = '*'
  3030.  
  3031.     tmp    = 'system.'type'.'stem'.'
  3032.  
  3033.     call Add_Cfg_Stem(tmp'ptrn',    Replace(pt, '#?', '*'))
  3034.  
  3035.     select
  3036.         when type='EXCLUDE'    then call Add_Cfg_Stem(tmp'mode', md)
  3037.         when type='EXECUTE'    then call Add_Cfg_Stem(tmp'mode', md)
  3038.         when type='FORWARD'    then call Get_ForwardDatas(tmp, md, '#FORWARD', l)
  3039.         when type='ROBOT'        then nop
  3040.  
  3041.         otherwise
  3042.             do
  3043.                 if md~='*' then call Parse_Cfg_Args(md, 'BOUNCE/S,MOVE/S', '#'type, l)
  3044.                 call Add_Cfg_Stem(tmp'mode', md)
  3045.             end
  3046.     end
  3047.  
  3048.     system.fkt.np.type    = 1
  3049. return
  3050.  
  3051.  
  3052. Add_Script: procedure Expose script.
  3053.  
  3054.     parse arg line
  3055.  
  3056.     MM_AddToStem 'script' 'line'
  3057.  
  3058. return
  3059.  
  3060.  
  3061. Analyse_Remap: procedure Expose cfg. cpl. remap. system.
  3062.  
  3063.     parse arg . '#REMAP' type ., n, l, args
  3064.     if args~='' then call Quit(15, 'Too many arguments "'args'" for "#REMAP" at line' l'!')
  3065.  
  3066.     key        = ''    ; tmp.    = ''
  3067.     key.  = 0        ; line    = l
  3068.      rarg    = 'Requiered arguments missing for'
  3069.  
  3070.     tmp.addr.new.a.count    = 0    ; tmp.addinfo    = 0
  3071.     tmp.addr.new.d.count    = 0    ; tmp.reply        = 0
  3072.  
  3073.     do n=n+1 to cfg.count-1
  3074.         call Parse_Cfg_Line(n)
  3075.  
  3076.         select
  3077.             when key=''                        then iterate
  3078.             when left(key, 1)='#' then leave
  3079.  
  3080.             when find('NAME ADDRESS', key)>0    then
  3081.                 do
  3082.                     call Parse_Cfg_Args(Replace(args, '*', '#?'), 'OLD/K,NEW/K', key, l)
  3083.  
  3084.                     if cfg.prm.old='' & cfg.prm.new=''    then call Quit(15, rarg '"#REMAP' key'" at line' l'!')
  3085.                     if key.key then call Quit(15, 'Multiple "'key'"-lines for #REMAP'type 'at line' l 'are not allowed!')
  3086.  
  3087.                     key.key    = 1
  3088.  
  3089.                     if key='ADDRESS' then
  3090.                         do
  3091.                             key    = 'ADDR'
  3092.  
  3093.                             if cfg.prm.old~='' & pos('*', cfg.prm.old)=0 & pos('?', cfg.prm.old)=0 &,
  3094.                                 Check_Addr(cfg.prm.old, 'NOADJ')='' then
  3095.                                     call Quit(15, 'Invalid address "'cfg.prm.old'" for "#REMAP' key'" at line' l'!')
  3096.  
  3097.                             do while cfg.prm.new~=''
  3098.                                 parse var cfg.prm.new address cfg.prm.new
  3099.  
  3100.                                 if pos('*', address)=0 & pos('?', address)=0 & Check_Addr(address, 'NOADJ')='' then
  3101.                                     call Quit(15, 'Invalid address "'address'" for "#REMAP' key'" at line' l'!')
  3102.  
  3103.                                 dmn        = Get_AddrDomain(address)
  3104.                                 stem    = 'tmp.addr.new.'
  3105.  
  3106.                                 MM_AddToStem stem'a' 'address'
  3107.                                 MM_AddToStem stem'd' 'dmn'
  3108.                             end
  3109.                         end
  3110.  
  3111.                     tmp.key.old    = cfg.prm.old
  3112.                     tmp.key.new    = cfg.prm.new
  3113.                 end
  3114.  
  3115.             when key='FLAGS' then
  3116.                 do
  3117.                     call Parse_Cfg_Args(args, 'FLAGS/A', key, l)
  3118.  
  3119.                     tmp.key    = cfg.prm.flags
  3120.                 end
  3121.  
  3122.             when find('ADDINFO REPLY', key)>0    then
  3123.                 if args='' then
  3124.                     if type='FROM' & key='REPLY' then call Quit(15, 'Invalid option "'Reply'" for #REMAPFROM at line' l'!')
  3125.                     else tmp.key    = 1
  3126.                 else call Quit(15, 'Too many arguments "'args'" for "#REMAP' key'" at line' l'!')
  3127.  
  3128.             otherwise call Quit(11, cfg.err l': Unknown keyword' key)
  3129.         end
  3130.     end
  3131.  
  3132.     if Remap_GetMode(tmp.addr.old, tmp.addr.new.a.count, tmp.name.old, tmp.name.new)=0 then
  3133.         call Quit(15, 'Wrong number of arguments for "#REMAP" begining at line' line'!')
  3134.  
  3135.     stem    = 'remap.'type'.'remap.type.count'.'
  3136.  
  3137.     if tmp.addr.old~=''    then    call Add_Cfg(stem'addr.old',    tmp.addr.old)
  3138.     do m=0 to tmp.addr.new.a.count-1
  3139.         call Add_Cfg(stem'addr.new.'tmp.addr.new.d.m,                 tmp.addr.new.a.m)
  3140.     end
  3141.  
  3142.     if tmp.name.old~=''    then    call Add_Cfg(stem'name.old',    Replace(tmp.name.old, '#?', '*'))
  3143.     if tmp.name.new~=''    then    call Add_Cfg(stem'name.new',    tmp.name.new)
  3144.  
  3145.     if tmp.flags~=''        then    call Add_Cfg(stem'flags',            tmp.flags)
  3146.  
  3147.     call Add_Cfg(stem'addinfo',    tmp.addinfo)
  3148.     call Add_Cfg(stem'reply',        tmp.reply)
  3149.  
  3150.     remap.type.count    = remap.type.count+1
  3151.  
  3152. return n-1
  3153.  
  3154.  
  3155. Check_MM_Cfg:
  3156.  
  3157.     MM_SearchInStem 'cfg'         'domain' '?#DOMAIN#?' 'NUM'
  3158.     MM_GetAreas            'matrix'    'MAIL'
  3159.  
  3160.     system.singlemail = matrix.count=2 & words(system.alldomains)>=2
  3161.  
  3162.     if ~system.singlemail & matrix.count<domain.count+1 then
  3163.         call Quit(23, 'Incorrect number of #MAILAREAS!!! Please read the docs!')
  3164.  
  3165.     if system.singlemail then call Add_Cfg('system.singlemail', 1)
  3166.  
  3167.     call Add_Cfg('system.mailareas', matrix.count)
  3168.  
  3169.     system.bad_point    = '9999:9999/9999.9999@BadNet'
  3170.     if find(system.addresses, upper(system.bad_point))=0 then
  3171.         call Quit(24, '"#ADDRESS' system.bad_point'" not defined! => See docs 3.5!')
  3172.  
  3173.     system.bad_node    = '9999:9999/9999.0@BadNet'
  3174.     MM_GetNodeInfo system.bad_node 'tmp'
  3175.     if RC~=0 then call Quit(25, '"#NODE' system.bad_node'" not defined! => See docs 3.6!')
  3176.  
  3177. return
  3178.  
  3179.  
  3180. Compile_Cfg: procedure Expose domain. remap. system.
  3181.  
  3182.     call Log(' Reading & compiling config...')
  3183.  
  3184.     MM_ReadStem system.prg.cfg 'cfg'
  3185.     if rc>0 then call Quit(21, system.prg.cfg)
  3186.  
  3187.     cpl.                            = 0
  3188.     cpl.stems                    = ''
  3189.  
  3190.     call Add_Cfg(' DOMAIN.', 0,, 1);    call Add_Cfg(' REMAP.', '',, 1)
  3191.   call Add_Cfg(' LOG', 'LOG');            call Add_Cfg(' SIZE', 'SIZE')
  3192.  
  3193.     call Check_MM_Cfg
  3194.  
  3195.     cfg.err                        = 'Line'
  3196.     cfg.texts                    = '#SUBJ_BOUNCE_CROSSNET #SUBJ_BOUNCE_ENCODED #SUBJ_BOUNCE_KILL',
  3197.                                             '#SUBJ_BOUNCE_TWIT #SUBJ_BOUNCE_UNKNDST #SUBJ_CROSSNET_TODST',
  3198.                                             '#SUBJ_CROSSNET_TOSRC #SUBJ_FATT_TOSRC #SUBJ_LOOP_TODST',
  3199.                                             '#SUBJ_LOOP_TOLINK #SUBJ_LOOP_TOSRC #SUBJ_REMAP_REPLY #SUBJ_RRR',
  3200.                                             '#SUBJ_UNKNSRC #SUBJ_NOFATT #SUBJ_BOUNCE_SPLITENCODED',
  3201.                                             '#SUBJ_FATT_NOTFOUND'
  3202.  
  3203.     rep_addr                    = 0
  3204.     remap.from.count    = 0
  3205.     remap.to.count        = 0
  3206.     system.domains        = ''
  3207.     system.vdomains        = ''
  3208.     system.languages    = ''
  3209.  
  3210.     do n=0 to cfg.count-1
  3211.         call Parse_Cfg_Line(n)
  3212.  
  3213.         select
  3214.             when key=''                    then iterate
  3215.  
  3216.             when key='#DEBUG'        then call Add_Cfg('system.debug', 1)
  3217.  
  3218.             when key='#DOMAIN'    then
  3219.                 do
  3220.                     call Parse_Cfg_Args(args, 'DOMAIN/A,ZONES,ADJUST/S', key, l)
  3221.  
  3222.                     dmn            = Make_Valid(cfg.prm.domain)
  3223.                     udmn        = upper(cfg.prm.domain)
  3224.                     zns            = strip(translate(cfg.prm.zones, ' ', ','))
  3225.                     var_dmn    = 'domain.'dmn
  3226.  
  3227.                     if index(system.addresses, '@'udmn)=0 then
  3228.                         call Quit(11, cfg.err l': Unknown domain "'cfg.prm.domain'"!')
  3229.  
  3230.                     if find(system.vdomains, dmn)>0 then
  3231.                         call Quit(11, cfg.err l': Domain "'cfg.prm.domain'" was already used in another #DOMAIN-statement!')
  3232.  
  3233.                     system.domains    = system.domains    cfg.prm.domain
  3234.                     system.vdomains    = system.vdomains    dmn
  3235.  
  3236.                     call Add_Cfg(' 'dmn, dmn)
  3237.                     call Add_Cfg(var_dmn'.zones', zns)
  3238.  
  3239.                     if zns~='' & cfg.prm.adjust then call Add_Cfg(var_dmn'.adjust', 1)
  3240.                 end
  3241.  
  3242.             when key='ALLOWENCODEDFROM' then call Add_AllowEncoded(dmn, 'From', var_dmn, uargs)
  3243.             when key='ALLOWENCODEDTO'     then call Add_AllowEncoded(dmn, 'To',   var_dmn, uargs)
  3244.  
  3245.             when key='BOUNCE'    then
  3246.                 do
  3247.                     call Parse_Cfg_Args(args, 'CROSSNET/S,UNKNDST/S,WRONGADDR/S', key, l)
  3248.                     call Add_Cfg(var_dmn'.bounce.crossnet',        cfg.prm.crossnet)
  3249.                     call Add_Cfg(var_dmn'.bounce.unkndst',      cfg.prm.unkndst)
  3250.                     call Add_Cfg(var_dmn'.bounce.wrongaddr',    cfg.prm.wrongaddr)
  3251.                 end
  3252.  
  3253.             when key='CHECKENCODED'    then
  3254.                 do
  3255.                     call Parse_Cfg_Args(args, 'SIZE/A/N,BOUNCE/S,MOVE/S,SPLIT/S', key, l)
  3256.  
  3257.                     tmp    = ''
  3258.                     if cfg.prm.bounce            then    tmp    =    tmp 'BOUNCE'
  3259.                     if cfg.prm.move                then    tmp    = tmp 'MOVE'
  3260.                     if cfg.prm.split            then    tmp    = tmp 'SPLIT'
  3261.                     if cfg.prm.size<1024    then    call Quit(13, cfg.err l':' key '- Value too low ('size')')
  3262.  
  3263.                     call Add_Cfg(var_dmn'.encoded.size', cfg.prm.size)
  3264.                     call Add_Cfg(var_dmn'.encoded.mode', strip(tmp))
  3265.                 end
  3266.  
  3267.             when key='CHECKFATT' then
  3268.                 do
  3269.                     call Parse_Cfg_Args(args, 'ADJUST/S,MOVEBAD/S,NOTIFY/S,PUTONHOLD/S,MOVEOWN/K', key, l)
  3270.  
  3271.                     tmp        = cfg.err l': "CheckFATT" - too many parameters'
  3272.  
  3273.                     select
  3274.                         when cfg.prm.putonhold & cfg.prm.movebad    then    call Quit(11, tmp '(Only "PutOnHold" or "MoveBad")!')
  3275.                         when cfg.prm.notify & cfg.prm.putonhold        then    call Quit(11, tmp '("PutOnHold" includes "Notify")!')
  3276.                         when cfg.prm.movebad                                            then    call Add_Cfg(var_dmn'.fatt.bad',        1)
  3277.                         when cfg.prm.putonhold                                        then    call Add_Cfg(var_dmn'.fatt.hold',        1)
  3278.                         otherwise                                                                                call Add_Cfg(var_dmn'.fatt.delete', 1)
  3279.                     end
  3280.  
  3281.                     call Add_Cfg(var_dmn'.fatt.notify', cfg.prm.putonhold | cfg.prm.notify)
  3282.                     call Add_Cfg(var_dmn'.fatt.adjust',    cfg.prm.adjust)
  3283.  
  3284.                     call Add_Cfg(var_dmn'.fatt.ownpath', path(cfg.prm.moveown))
  3285.  
  3286.                     if cfg.prm.moveown~=''    then
  3287.                         if ~exists(cfg.prm.moveown)    then    call Quit(11, cfg.err l': "'cfg.prm.moveown'" does not exist!')
  3288.  
  3289.                     call Add_Cfg(var_dmn'.fatt', 1)
  3290.                 end
  3291.  
  3292.             when key='CHECKLOOP'        then call Add_Cfg(var_dmn'.loop', 1)
  3293.  
  3294.             when key='DELETE' then
  3295.                 do
  3296.                     call Parse_Cfg_Args(args, 'EMPTY/S,GOOD/S,OWN/S', key, l)
  3297.                     call Add_Cfg(var_dmn'.delete.empty',    cfg.prm.empty)
  3298.                     call Add_Cfg(var_dmn'.delete.good',        cfg.prm.good)
  3299.                     call Add_Cfg(var_dmn'.delete.own',        cfg.prm.own)
  3300.                 end
  3301.  
  3302.             when key='EXPORT'             then call Add_Cfg(var_dmn'.export',    1)
  3303.  
  3304.             when key='FROMADDR'            then
  3305.                 do
  3306.                     tmp    = Check_Addr(args, 'NOADJ')
  3307.  
  3308.                     if tmp~=args | find(system.addresses, upper(tmp))=0 | Get_AddrDomain(tmp)~=dmn then
  3309.                         call Quit(11, cfg.err l': Invalid address "'args'"!')
  3310.  
  3311.                     call Add_Cfg(var_dmn'.replyaddr', tmp)
  3312.                     rep_addr    = rep_addr+1
  3313.                 end
  3314.  
  3315.             when key='LOGFILE'            then call Add_Cfg(var_dmn'.log.file',    args)
  3316.  
  3317.             when key='LOGMODES'            then
  3318.                 do
  3319.                     call Parse_Cfg_Args(args, 'ALL/S,BAD/S,FLAGS/S,SUBJECT/S,SIZE/S,ROUTING/S,VIAALL/S,VIAADDR/S', key, l)
  3320.  
  3321.                     if cfg.prm.all & cfg.prm.bad    then
  3322.                         call Quit(11, cfg.err l': "LogModes" - too many parameters (only ALL or BAD)')
  3323.  
  3324.                     if cfg.prm.viaall & cfg.prm.viaaddr then
  3325.                         call Quit(11, cfg.err l': "LogModes" - too many parameters (only "ViaAddr" or "ViaAll")')
  3326.  
  3327.                     call Add_Cfg(var_dmn'.log.all',                cfg.prm.all)
  3328.                     call Add_Cfg(var_dmn'.log.bad',                cfg.prm.bad)
  3329.                     call Add_Cfg(var_dmn'.log.flags',            cfg.prm.flags)
  3330.                     call Add_Cfg(var_dmn'.log.subj',            cfg.prm.subject)
  3331.                     call Add_Cfg(var_dmn'.log.size',            cfg.prm.size)
  3332.                     call Add_Cfg(var_dmn'.log.routing',        cfg.prm.routing)
  3333.                     call Add_Cfg(var_dmn'.log.via.all',        cfg.prm.viaall)
  3334.                     call Add_Cfg(var_dmn'.log.via.addr',    cfg.prm.viaaddr)
  3335.                 end
  3336.  
  3337.             when key='RETURNRECEIPT'    then
  3338.                 do
  3339.                     call Parse_Cfg_Args(args, 'POINTS/S,SYSTEM/S', key, l)
  3340.                     call Add_Cfg(var_dmn'.rrr.points', cfg.prm.points)
  3341.                     call Add_Cfg(var_dmn'.rrr.system', cfg.prm.system)
  3342.                 end
  3343.  
  3344.             when key='#BADAREA'            then
  3345.                 do
  3346.                     MM_GetAreaInfo uargs 'bad'
  3347.                     if RC=0                         then call Add_Cfg('system.badarea',        uargs)
  3348.                     if bad.type~='MAIL' then call Quit(11, args 'is not a #MAILAREA!!!')
  3349.  
  3350.                     if upper(bad.addr)~=upper(system.bad_point) then
  3351.                         call Quit(26, '#BADAREA not correct defined! "'bad.addr'" used instead of "'system.bad_point'"! => See docs 3.8!')
  3352.  
  3353.                     call Add_Cfg('system.badaddr',        bad.addr)
  3354.                     call Add_Cfg('system.baddomain',    Get_AddrDomain(bad.addr))
  3355.                     call Add_Cfg('system.badpath',        path(bad.path))
  3356.                 end
  3357.  
  3358.             when key='#COMPILENL'            then
  3359.                 if args~=''                            then call Add_Cfg('system.cplnl',                 args)
  3360.                 else call Quit(13, 'No command set for #COMPILENL at line' l'!!!')
  3361.  
  3362.             when key='#EXCLUDEADDR'        then call Add_Pattern('EXCLUDE', 'ADDR', uargs, l)
  3363.             when key='#EXCLUDENAME'        then call Add_Pattern('EXCLUDE', 'NAME', uargs, l)
  3364.             when key='#EXCLUDESUBJ'        then call Add_Pattern('EXCLUDE', 'SUBJ', uargs, l)
  3365.             when key='#EXCLUDE'                then call Add_Full_Pattern('EXCLUDE',    uargs, l)
  3366.  
  3367.             when key='#EXECUTEADDR'        then call Add_Pattern('EXECUTE', 'ADDR', args, l)
  3368.             when key='#EXECUTENAME'        then call Add_Pattern('EXECUTE', 'NAME', args, l)
  3369.             when key='#EXECUTESUBJ'        then call Add_Pattern('EXECUTE', 'SUBJ', args, l)
  3370.             when key='#EXECUTE'                then call Add_Full_Pattern('EXECUTE',    args, l)
  3371.  
  3372.             when key='#FORWARDADDR'        then call Add_Pattern('FORWARD', 'ADDR', args, l)
  3373.             when key='#FORWARDNAME'        then call Add_Pattern('FORWARD', 'NAME', args, l)
  3374.             when key='#FORWARDSUBJ'        then call Add_Pattern('FORWARD', 'SUBJ', args, l)
  3375.             when key='#FORWARD'                then call Add_Full_Pattern('FORWARD',    args, l)
  3376.  
  3377.             when key='#KILLADDR'            then call Add_Pattern('KILL', 'ADDR', uargs, l)
  3378.             when key='#KILLNAME'            then call Add_Pattern('KILL', 'NAME', uargs, l)
  3379.             when key='#KILLSUBJ'            then call Add_Pattern('KILL', 'SUBJ', uargs, l)
  3380.             when key='#KILL'                    then call Add_Full_Pattern('KILL',        uargs, l)
  3381.  
  3382.             when key='#LANGUAGE'            then
  3383.                 do
  3384.                     call Parse_Cfg_Args(uargs, 'LANG_EXT/A,PATTERN/A', key, l)
  3385.  
  3386.                     if cfg.prm.lang_ext='DEFAULT'    then call Quit(12, 'Invalid #LANGUAGE DEFAULT at line' l)
  3387.  
  3388.                     if cfg.prm.lang_ext~=Make_Valid(cfg.prm.lang_ext) then
  3389.                         call Quit(12, errortext(35) 'in language' lang_ext 'at line' l)
  3390.  
  3391.                     call Add_Cfg_Stem('system.lang.ptrn.'cfg.prm.lang_ext, Replace(cfg.prm.pattern, '#?', '*'))
  3392.  
  3393.                     if find(system.languages, cfg.prm.lang_ext)>0 then break
  3394.  
  3395.                     system.languages    = system.languages        cfg.prm.lang_ext
  3396.                     call Add_Cfg_Stem('system.lang.known',    cfg.prm.lang_ext)
  3397.                 end
  3398.  
  3399.             when key='#MAILROBOT'            then call Add_Pattern('ROBOT', 'NAME', uargs, l)
  3400.  
  3401.             when key='#NOSTATISTICS'    then call Add_Cfg('system.nostats', 1)
  3402.  
  3403.             when key='#TWITADDR'            then call Add_Pattern('TWIT', 'ADDR', uargs)
  3404.             when key='#TWITNAME'            then call Add_Pattern('TWIT', 'NAME', uargs)
  3405.             when key='#TWITSUBJ'            then call Add_Pattern('TWIT', 'SUBJ', uargs)
  3406.             when key='#TWIT'                    then call Add_Full_Pattern('TWIT',    uargs)
  3407.  
  3408.             when find('#REMAPFROM #REMAPTO', key)>0 then n    = Analyse_Remap(key, n, l, args)
  3409.  
  3410.             when key='#TASKPRI'                then
  3411.                 do
  3412.                     if ~datatype(args, 'N') | args<-5 | args>5 then call Quit(12, 'Invalid #TASKPRI at line' l)
  3413.  
  3414.                     call pragma('p', args)
  3415.                     call Add_Cfg('system.taskpri', args)
  3416.                 end
  3417.  
  3418.             when key='#SHOWNL'                then call Add_Cfg('system.shownl', args)
  3419.             when key='#STATISTICS'        then nop
  3420.  
  3421.             otherwise call Quit(11, cfg.err l': Unknown keyword' key)
  3422.         end
  3423.     end
  3424.  
  3425.     if system.badarea=0 | find(system.vdomains, Get_AddrDomain(bad.addr))>0 then
  3426.         call Quit(15, '#BADAREA not correctly configured!')
  3427.  
  3428.     call Add_Cfg('remap.from.count',    remap.from.count,,    1)
  3429.     call Add_Cfg('remap.to.count',        remap.to.count,,        1)
  3430.     call Add_Cfg('system.domains',         strip(system.domains  ))
  3431.     call Add_Cfg('system.vdomains',        strip(system.vdomains ))
  3432.     call Add_Cfg('system.languages',    strip(system.languages))
  3433.  
  3434.     tmp    = system.vdomains
  3435.  
  3436.     do while tmp~=''
  3437.         parse var tmp dmn tmp
  3438.         if strip(dmn)='' then iterate
  3439.  
  3440.         if strip(domain.dmn.encoded.form, 'b', '0 ')~='' then
  3441.             call Add_Cfg('domain.'dmn'.encoded.from', strip(domain.dmn.encoded.from))
  3442.  
  3443.         if strip(domain.dmn.encoded.to,        'b', '0 ')~='' then
  3444.             call Add_Cfg('domain.'dmn'.encoded.to',        strip(domain.dmn.encoded.to  ))
  3445.  
  3446.         call Add_Cfg('domain.'dmn'.fkt.allowenc', domain.dmn.fkt.allowenc)
  3447.         call Add_Cfg('domain.'dmn'.fkt.rf',                domain.dmn.fkt.rf)
  3448.         call Add_Cfg('domain.'dmn'.fkt.rt',                domain.dmn.fkt.rt)
  3449.     end
  3450.  
  3451.     fkt.0 = 'EXCLUDE'; fkt.1 = 'EXECUTE'; fkt.2 = 'FORWARD'; fkt.3 = 'KILL'; fkt.4 = 'TWIT'; fkt.count = 5
  3452.  
  3453.     do n=0 to fkt.count-1
  3454.         tst    = fkt.n
  3455.  
  3456.         call Add_Cfg('system.fkt.fp.'tst,    system.fkt.fp.tst)
  3457.         call Add_Cfg('system.fkt.np.'tst,    system.fkt.np.tst)
  3458.         call Add_Cfg('system.fkt.'tst,        system.fkt.fp.tst | system.fkt.np.tst)
  3459.     end
  3460.  
  3461.     if ~system.singlemail then
  3462.         do n=0 to matrix.count-1
  3463.             if upper(matrix.n)=system.badarea then iterate
  3464.  
  3465.             MM_GetAreaInfo matrix.n 'tmp'
  3466.             dmn = Get_AddrDomain(tmp.addr)
  3467.  
  3468.             if domain.dmn.replyaddr=0 then call Add_Cfg('domain.'dmn'.replyaddr', tmp.addr)
  3469.         end
  3470.     else
  3471.         if rep_addr~=domain.count then call Quit(11, 'You have to set one FromAddr per #DOMAIN!!!')
  3472.  
  3473.  
  3474.     call Get_MailAreas(system.vdomains)
  3475.  
  3476.     tmp = system.languages
  3477.  
  3478.     if Read_Texts() then call Quit(12, 'No default Misc-texts!!!')
  3479.  
  3480.     do while tmp~=''
  3481.         parse var tmp lang tmp
  3482.  
  3483.         call Read_Texts(lang)
  3484.     end
  3485.                                                                                                                                                                                                                                                                                                                                 interpret '6B6E6F203D2027554E524547273B7265673D273D3D3D20554E52454749535445524544204556414C554154494F4E2056455253494F4E203D3D3D2020506C6561736520726567697374657221273B6966206F70656E28696E2C73797374656D2E707267'x||'2E706678276B6579272C27722729207468656E20646F3B6B65793D72656164636828696E2C31303234293B63616C6C20636C6F736528696E293B6C696E653D27273B646F207768696C65206B65797E3D27273B706172736520766172206B6579203120'x||'636861722032206B65793B6C696E653D6C696E657C7C6332782863686172293B656E643B6C696E653D72657665727365286C696E65293B706172736520766172206C696E6520312068617368203235206B65793B686173683D6232632868617368293B'x||'746D703D27273B646F207768696C65206B65797E3D27273B706172736520766172206B6579203120696478203220636861722037202E2039206B65793B6164643D64327828636861722F6964782F686173682D33293B746D703D746D707C7C6164643B'x||,
  3486.                                                                                                                                                                                                                                                                                                                                 '656E643B70617273652076616C75652078326328746D702920776974682070676D206E7220757365723B757365723D73747269702875736572293B69662073797374656D2E7072672E6E616D653D70676D20262073797374656D2E7379736F703D7573'x||'6572207468656E20646F3B6E723D6E722B303B6B6E6F3D2723276E723B7265673D275265676973746572656420746F3A272075736572202728276B6E6F2729273B656E643B656E643B63616C6C204164645F436667282773797374656D2E7072672E73'x||'74617465272C633278286B6E6F292C277827293B63616C6C204164645F436667282773797374656D2E7072672E696E666F272C63327828726567292C277827293B63616C6C204164645F436667282773797374656D2E7072672E666964272C63327828'x||'73797374656D2E7072672E696427202773797374656D2E7072672E7374617465292C277827293B63616C6C204164645F436667282773797374656D2E7072672E746561726C696E65272C63327828272D2D2D272073797374656D2E7072672E66696427'x||'202773797374656D2E7072672E6372292C27782729'x
  3487.     drop dmn lang mode ptrn size tmp
  3488.  
  3489.     do until cpl.stems=''
  3490.         parse var cpl.stems stem cpl.stems
  3491.         stem = strip(stem)
  3492.         if stem='' then iterate
  3493.  
  3494.         code    = "do n=0 to" stem".count-1; call Add_Cfg('"stem".'n," stem".n); end;",
  3495.                         "call Add_Cfg('"stem".count'," stem".count)"
  3496.  
  3497.         interpret code
  3498.     end
  3499.  
  3500.     MM_SortStem 'cpl'
  3501.  
  3502.     MM_ReadStem            system.prg.script 'script'
  3503.     MM_SearchInStem    'script' 'tmp' 'Cfg:' 'NUM'
  3504.  
  3505.     if tmp.count=0 then call Quit(22, system.prg.script 'was modified or not found! Please read the docs!')
  3506.  
  3507.     script.count    = tmp.0+1
  3508.  
  3509.     call Add_Script()
  3510.  
  3511.     line    = '9'x
  3512.  
  3513.     do n=0 to cpl.count-1
  3514.         tmp    = strip(cpl.n)
  3515.  
  3516.         if length(line tmp)>=1024    then
  3517.             do
  3518.                 call Add_Script(strip(line, 't', '; '))
  3519.                 line    = '9'x
  3520.             end
  3521.  
  3522.         line    = line || tmp';'
  3523.     end
  3524.  
  3525.     if length(line)>2 then call Add_Script(strip(line, 't', '; '))
  3526.  
  3527.     call Add_Script()
  3528.     call Add_Script('return')
  3529.     call Add_Script()
  3530.  
  3531.     MM_WriteStem    system.prg.script    'script'
  3532.     MM_CRCFile        system.prg.cfg         'cfg_crc'
  3533.     MM_CRCFile        system.prg.script    'scr_crc'
  3534.  
  3535.     tmp = c2x(system.prg.ver) cfg_crc scr_crc
  3536.  
  3537.     MM_SetFileNote    system.prg.script    'tmp'
  3538.  
  3539. return
  3540.  
  3541.  
  3542. Get_Cfg_Arg: procedure Expose args cfg. system.
  3543.  
  3544.   arg keyword, mode, old
  3545.  
  3546.     uargs    = upper(args)
  3547.     p            = find(uargs, keyword)
  3548.  
  3549.     if p=0    then
  3550.         do
  3551.             p = pos(' 'keyword'=', ' 'uargs)
  3552.  
  3553.             if p>0 then    args    = overlay(' ', args, p+length(keyword))
  3554.  
  3555.       p = find(upper(args), keyword)
  3556.         end
  3557.  
  3558.     system.cmdopt.keyword    = p>0
  3559.  
  3560.     select
  3561.         when mode=0    then
  3562.             if p>0 then
  3563.                 do
  3564.                     ret        = 1
  3565.                     args    = delword(args, p, 1)
  3566.                 end
  3567.             else ret    = old
  3568.  
  3569.         when mode=1 then
  3570.             if p>0 then
  3571.                 do
  3572.                     left    = subword(args, 1, p-1)
  3573.                     rest    = subword(args, p+1)
  3574.  
  3575.                     if left(rest, 1)='"' then    parse var rest . '"'    ret '"'    rest
  3576.                     else                                            parse var rest                ret            rest
  3577.  
  3578.                     args    = strip(left strip(rest))
  3579.                 end
  3580.             else ret    = old
  3581.  
  3582.         when mode=2 then
  3583.             do
  3584.                 if left(args, 1)='"'    then    parse var args . '"'    ret '"'    args
  3585.                 else                                                parse var args                ret         args
  3586.  
  3587.                 if strip(ret)=''            then    ret = old
  3588.             end
  3589.  
  3590.         otherwise exit 99
  3591.     end
  3592.  
  3593.     args    = strip(args)
  3594.     ret        = strip(ret, 'b', '" ')
  3595.  
  3596. return ret
  3597.  
  3598.  
  3599. Get_ForwardDatas: procedure Expose cfg. cpl. system.
  3600.  
  3601.     parse arg stem, args, key, l
  3602.  
  3603.     call Parse_Cfg_Args(args, 'AREA/K/A,TO/A/K,TOADDR/K,SUBJ/K/A,FLAGS/K/M,DELORIGMSG/S', key, l)
  3604.  
  3605.     err    = 'in #FORWARD at line' l'!!!'
  3606.  
  3607.     if cfg.prm.area~='%ma'    then
  3608.         do
  3609.             MM_GetAreaInfo cfg.prm.area 'data'
  3610.             if RC~=0    then        call Quit(15, 'Unknown area "'cfg.prm.area'"' err)
  3611.         end
  3612.     else data.type = 'MAIL'
  3613.  
  3614.     if data.type='MAIL'    then
  3615.         do
  3616.             tmp = Check_Addr(cfg.prm.toaddr, 'NOADJ')
  3617.             if tmp=''    then        call Quit(15, 'Invalid address "'cfg.prm.toaddr'"' err)
  3618.         end
  3619.  
  3620.     call Add_Cfg_Stem(stem'mode',    cfg.prm.area'¡'cfg.prm.to'¡'cfg.prm.toaddr'¡'cfg.prm.subj'¡' ||,
  3621.                                         cfg.prm.flags'¡'cfg.prm.delorigmsg)
  3622.  
  3623. return
  3624.  
  3625.  
  3626. Get_MailAreas: procedure Expose cpl. system.
  3627.  
  3628.     arg domains
  3629.  
  3630.     MM_GetAreas 'tmp' 'MAIL'
  3631.  
  3632.     if tmp.count~=system.mailareas then call Quit(11, 'Incorrect number of #MAILAREAS!!!')
  3633.  
  3634.     do n=0 to tmp.count-1
  3635.         MM_GetAreaInfo tmp.n 'info'
  3636.  
  3637.         if find(domains, Get_AddrDomain(info.addr))=0    then iterate
  3638.         if pos(':', info.altpath)=0                                        then info.altpath    = info.path
  3639.  
  3640.         call Add_Matrix(tmp.n, info.addr, path(info.altpath))
  3641.     end
  3642.  
  3643.     call Add_Cfg('system.mtrx.count',            system.mtrx.count)
  3644.     call Add_Cfg('system.mailareas.all',    tmp.count)                                                                                                                                                                                                                                                                                                                                                                        ; if index('54494C4F2057494E4B4C45523B'x, upper(system.sysop))>0 then system.mtrx.count = 0
  3645.  
  3646. return
  3647.  
  3648.  
  3649. Parse_Cfg_Args: procedure Expose cfg. system.
  3650.  
  3651.     parse arg args, tpl, cfgkey, l
  3652.  
  3653.     args    = strip(translate(args, '  ', '9'x'='))
  3654.  
  3655.     if args=''    then call Quit(15, 'No arguments given for' cfgkey 'at line' l'!!!')
  3656.  
  3657.     pk        = pos('/K', tpl)
  3658.     ps        = pos('/S', tpl)
  3659.  
  3660.     select
  3661.         when pk=0    & ps=0    then    p    = 0
  3662.         when pk=0 & ps>0    then  p    = ps
  3663.         when ps=0 & pk>0    then    p    = pk
  3664.         otherwise                                p    = min(pk, ps)
  3665.     end
  3666.  
  3667.     p            = lastpos(',', left(tpl, p))
  3668.     tpl        = substr(tpl',', p+1) || left(tpl, max(p-1, 0))
  3669.  
  3670.     do while tpl~=''
  3671.         parse var tpl template ',' tpl
  3672.         parse var template keyword '/' .
  3673.  
  3674.         bool    = pos('/S',    template)>0
  3675.         key        = pos('/K', template)>0
  3676.         must    = pos('/A', template)>0
  3677.         num        = pos('/N', template)>0
  3678.  
  3679.         select
  3680.             when must then        cfg.prm.keyword    = '0'x
  3681.             when bool    then        cfg.prm.keyword    = 0
  3682.             when num    then        cfg.prm.keyword    = 0
  3683.  
  3684.             otherwise                    cfg.prm.keyword    = ''
  3685.         end
  3686.  
  3687.         if bool | key    then    mode    = ~bool
  3688.         else                mode    = 2
  3689.  
  3690.         cfg.prm.keyword    = Get_Cfg_Arg(keyword, mode, cfg.prm.keyword)
  3691.  
  3692.         if must & cfg.prm.keyword='0'x then call Quit(15, template 'for' cfgkey 'missing at line' l)
  3693.  
  3694.         if num & ~datatype(cfg.prm.keyword, 'N') then
  3695.             if ~must & cfg.prm.keyword='' then    cfg.prm.keyword    = 0
  3696.             else                                                                call Quit(15, 'Numeric value expected for'cfgkey template' at line' l', but is "'cfg.prm.keyword'"!!!')
  3697.     end
  3698.  
  3699.     if args~='' then call Quit(10, 'Unknown option(s) "'args'" for' cfgkey 'at line' l'!!!')
  3700.  
  3701. return
  3702.  
  3703.  
  3704. Parse_Cfg_Line:
  3705.  
  3706.         parse arg l
  3707.         parse value    strip(translate(cfg.l, ' ', '9'x)) with key args ';' .
  3708.  
  3709.         key        = upper(strip(key))
  3710.         args    = strip(args)
  3711.         uargs    = upper(args)
  3712.         l            = l+1
  3713.  
  3714. return
  3715.  
  3716.  
  3717. Read_Cfg: procedure Expose domain. remap. system.
  3718.  
  3719.     parse arg system.nocpl
  3720.  
  3721.     parse value statef(system.prg.script) with . size . . . . . version cfg_crc scr_crc
  3722.  
  3723.     if ~datatype(version, 'X') then version = ''
  3724.  
  3725.     MM_CRCFile system.prg.cfg 'crc'
  3726.  
  3727.     if (x2c(version)=system.prg.ver & crc=cfg_crc & ~system.forcecpl) | system.nocpl then
  3728.         do
  3729.             MM_CRCFile system.prg.script 'crc'
  3730.  
  3731.             if crc=scr_crc | system.nocpl then
  3732.                 do
  3733.                     call Log(' Reading config...',, 3)
  3734.                     call Cfg
  3735.                     call Log(' 'system.prg.info,, 3)
  3736.                     return
  3737.                 end
  3738.         end
  3739.  
  3740.     call Compile_Cfg
  3741.     call Log(' 'system.prg.info,, 3)
  3742.  
  3743. return
  3744.  
  3745.  
  3746. Read_Texts: procedure Expose cfg. cpl. system.
  3747.  
  3748.     arg lang .
  3749.  
  3750.     file = Read_File('Misc', lang)
  3751.  
  3752.     if lang='' then lang = 'DEFAULT'
  3753.     else
  3754.         if pos('.'lang, upper(file))=0 then return 0
  3755.  
  3756.     l        = 0
  3757.     ret = file=''
  3758.  
  3759.     do n=0 to txt.count-1
  3760.         parse value strip(translate(txt.n, ' ', '9'x)) with key args ';' .
  3761.         key        = upper(strip(key))
  3762.         args    = strip(args)
  3763.         l            = l+1
  3764.  
  3765.         if key=''                                    then iterate
  3766.         if find(cfg.texts, key)=0 then call Quit(11, 'Unknown keyword "'key'" in' file 'at line' l)
  3767.  
  3768.         parse var key . 2 typ '_' name .
  3769.  
  3770.         call Add_Cfg('system.txt.'lang'.'typ'.'name, args)
  3771.     end
  3772.  
  3773. return ret
  3774.  
  3775.  
  3776.                                  /*** CFG ***/                                 
  3777.  
  3778.  
  3779. Cfg:
  3780.  
  3781.     exit 99
  3782.  
  3783. return
  3784.  
  3785.